最近比较想上进,又不知道要干什么,就来水几篇博客欺骗一下自己。
GPIO全称是:General Purpose Input / Output ,是stm32用于控制输入和输出信号的通用接口。我们用的MCU都有这玩意,比如STM32F103C8T6上有 GPIOA,GPIOB,GPIOC,这些ABC等,把GPIO分成了不同的组,每组端口为0~15,一般不同的芯片,分组也不一样。
这里我们主要学习GPIO的输入,输出,还有有些时候的引脚复用,因而对不同芯片的引脚排布不做过多的讨论。我们将通过几个例程来学习IO口的输入,输出,最后做出一个超级简单的小项目。
在介绍GPIO的输入输出之前,我想我有必要去再介绍一下GPIO的8种工作模式。这八种工作模式分为输入和输出,其中输入的模式有4种,分别是:上拉输入,下拉输入,浮空输入,模拟输入。对应的,我们同样有4种输出模式,分别叫: 推挽输出,开漏输出,和复用推挽输出和复用开漏输出。其中具体的结构,我在我的另一篇博客: stm32——详解GPIO的8种模式,不会你打我_学习stm32的gpio口控制-CSDN博客
中写的很详细,在这里不在过多描述,我这里只告诉你这玩意在标准库里的英文是什么,怎么在简单的项目中去玩,然后一般我们怎么去用,更深的理解需要你在日后的工作和学习中去积累。
其实说句实话,使用一个GPIO的模式,其实就是配置这条路上的各种寄存器,按道理来说配置寄存器应该是很麻烦的,你要翻着手册一页一页去看,一位一位的去写寄存器。但是我们用的是库函数啊,这玩意已经把配置每个寄存器的过程写好了,我们这里要做的不是去纠结为什么要这样配置,我们要学会的是如何运用这些函数。至于底层的东西,也需要你以后去理解。
下面我们开始介绍GPIO相关的函数。
首先,有关GPIO的文件,在stm32的标准库里,在“stm32f10x_gpio.c”和“stm32f10x_gpio.h”里。
那么我们在标准库里,是如何去定义配置GPIO的?
其实就是用的一个函数,这个函数是
GPIO_Init(GPIO_TypeDef * GPIOx , GPIO_InitTypeDef * GPIO_InitStruct)
这个函数有两个参数,第一个是需要初始化的GPIO组,我们可以选择GPIOA,GPIOB,GPIOC,等,说白了就是你要用的引脚属于的那个GPIO的组,比如我要用GPIOA的第2个引脚,那么就要初始化GPIOA。第二个参数是一个结构体指针,结构体的类型是 GPIO_InitTypeDef,他会用这个结构体里的参数去配置GPIO。其实说了这么多就一句话——就是第一个参数选择GPIO,第二个参数配置GPIO。下面我们来看这个结构体里的东西是什么我们可以找到这个结构体的定义,如图:
其实结构体里就三个成员,GPIO_Pin ,GPIO_Mode, GPIO_Speed
下面我们一个一个来说:
GPIO_Pin 就是选择引脚,你用哪个引脚就选择哪个:
其实就是从这一大堆里去选择。
这个GPIO_Mode,就是上边我说的GPIO的8种工作模式。这里需要你根据你做的工程去选择这个模式。 就是从这个枚举里去选择一个。
GPIO_Speed指的是输出频率,这个玩意我们一般没有特别要求,我们一般用50MHZ
其实说到这里, GPIO_Init(GPIO_TypeDef * GPIOx , GPIO_InitTypeDef * GPIO_InitStruct) 这个函数就讲完了,具体怎么写呢,我们可以在Keil里去看看
#include "stm32f10x.h" // 这个是stm32的头文件
首先我们要写头文件
然后我们可以定义一个结构体
GPIO_InitTypeDef GPIO_InitStructure; //这里定义这个结构体为GPIO_InitStructure
在c语言里我们可以用‘.’引出结构体成员
我们配置这个结构体,假定我用的GPIOA的第一个引脚,输入方式为上拉输入,频率给一个50MHz,此时我们应该进行对应的配置:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置为上拉输入;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //配置选择端口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置频率
当我们结构体配置好以后,就要去调用GPIO_Init()函数对IO口进行配置:
GPIO_Init(GPIOA,&GPIO_InitStructure);//调用GPIO_Init函数
到这里,我们的GPIO在理论上就初始化完成了,但是我们这里忽略了一步很重要的东西,就是开启时钟。这是因为STM32的外设和模块需要时钟信号才能正常工作。STM32的内部资源是由时钟控制的,默认情况下,为了节约能量,很多外设的时钟是关闭的。如果时钟没有开启,相应的外设将不会接收到时钟信号,无法进行初始化和工作。那我们开启时钟的又要用到一个函数,是
RCC_APB2PeriphResetCmd(),这玩意就是开启我们GPIOA时钟的,由于这个函数涉及到另一个部分RCC,在这里不多说,如果需要了解这玩意,可以去看看这俩玩意
我这里就直接说明如何去开启时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使用RCC开启GPIOA的时钟
那么我们把它综合起来,就得到了一个GPIOA初始化的代码
#include "stm32f10x.h" // Device header
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使用RCC开启GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStructure; //这里定义这个结构体为GPIO_InitStructure
/*用.可以引出结构体的成员*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置为上拉输入;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //配置选择端口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置频率
GPIO_Init(GPIOA,&GPIO_InitStructure);//调用GPIO_Init函数
}
下面我们就可以进行GPIO的读和写的操作,stm32中完成GPIO的读和写的函数有好几个,这里为了减少篇幅,我只提供一个读和写的,其余的可以去文件里看,或者去看stm32公司提供的固件库
我们通过两个小实验去完成,第一个就是 GPIO输出,我们用蜂鸣器去完成,现在要满足一个小项目,输出一个高电平,让蜂鸣器响
接线图大概能看懂哈
下边给出函数GPIO _SetBits
GPIO_SetBits(GPIOA,GPIO_Pin_1);//将GPIOA的PA1设置为高电平
这样我们把整个代码给出来
#include "stm32f10x.h" // Device header
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使用RCC开启GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStructure; //这里定义这个结构体为GPIO_InitStructure
/*用.可以引出结构体的成员*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置为上拉输入;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //配置选择端口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置频率
GPIO_Init(GPIOA,&GPIO_InitStructure);//调用GPIO_Init函数
while(1)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);//将GPIOA的PA1设置为高电平
}
}
while(1)这个循环,目的就是让其中的命令持续执行。·这里我的蜂鸣器是高电平响 ,编译下载后确实蜂鸣器响了 。而且实现很简单,就是我们初始化好GPIO后,直接让这个引脚输出高电平。
其实IO口输入也是同样的操作,不过就是换个函数罢了。很简单,把写的函数换成读的函数就行了,聪明的你肯定知道要怎么去改,其实就是把GPIO_SetBits(GPIOA,GPIO_Pin_1);换成一个
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1);就可以了,这些就你自己发挥一下聪明才智就行了。
然后我们在这里提一嘴GPIO的复用
stm32是不是引脚不是很多,但是作用很多,看这个图就可以知道为什么要复用哈,让有限数量的引脚实现更多的外设和功能。stm32的每个IO口都对应很多功能,一般情况下我们都将其作为IO口进行输入输出(其实只做输入输出用在项目中不常用)。但是有时候我会遇到引脚用的很满,或者要用某个引脚的特殊功能,如定时器,串口等,都要引脚复用,今天这里就不多说了,有感觉直接去看手册。
今天就到这里吧,下一篇博客就水一个单纯用GPIO输入和输出做的一个简易的烟雾报警器。