STM32速成笔记(二)—GPIO

简介: 本文介绍了STM32的GPIO的配置和使用方法,并且给出了应用实例。此外,针对使用时可能遇到的一些问题给出了解决办法。


🎀 文章作者:二土电子
🐸 期待大家一起学习交流!


一、什么是GPIO

GPIO(英语:General-purpose input/output),通用型之输入输出的简称,可以用来输入高低电平或者输出高低电平。这里的高电平指的是3.3V,低电平指的是0V。通常称GPIO为IO口,或者引脚。

STM32F103ZET6有GPIOx_0~GPIOx_15,其中x = A,B,C,D,E,F,G。

二、GPIO的输入/输出模式

GPIO有多种输入输出模式,输入模式有

  • 输入浮空
    输入浮空指的是GPIO与外设之间既不接高电平,也不接低电平,呈高阻态。除了类似于在数据传输时将GPIO配置为输入浮空外,一般不配置为该模式。因为输入浮空状态的GPIO电压具有不确定性,可能是0V,也可能是VCC,或者是介于0V和VCC之间的某一个值。
  • 输入上拉
    输入上拉是通过一个上拉电阻,将GPIO拉至高电平状态,不受外接电路的影响。
  • 输入下拉
    输入下拉是通过一个下拉电阻,将GPIO拉至低电平,不受外接电路的影响。
  • 模拟输入
    模拟输入模式通常用在ADC采集,采集模拟信号。

输出模式有

  • 开漏输出
  • 推挽式输出
  • 推挽式复用功能
  • 开漏复用功能

对于一些输出模式这里就不再做详细介绍了,贴一篇大佬的文章深刻理解GPIO。这些模式在接下来的学习过程中会慢慢的介绍这些模式需要在什么时候使用,这里只需要知道就够了。

三、GPIO初始化配置

本专栏介绍的是使用库函数进行开发,很多内容都是库函数提供的,相对来讲非常方便。在初始化GPIO时有一个结构体,只需要对这个结构体进行配置即可。结构体中包括想要配置的GPIO引脚,GPIO速度,GPIO工作模式。

初始化GPIO的步骤主要有

  • 定义GPIO结构体
  • 开启时钟
    GPIO工作需要提供时钟信号,在初始化结构体之前需要将时钟打开
  • 配置结构体成员
    GPIO_Pin是想要配置的IO,GPIO_Speed,通常写GPIO_Speed_50MHz,GPIO_Mode是IO的工作模式
  • 写入配置

GPIO的工作模式在程序中有定义

typedef enum
{
   
    GPIO_Mode_AIN = 0x0,   // 模拟输入
  GPIO_Mode_IN_FLOATING = 0x04,   // 输入浮空
  GPIO_Mode_IPD = 0x28,   // 输入下拉
  GPIO_Mode_IPU = 0x48,   // 输入上拉
  GPIO_Mode_Out_OD = 0x14,   // 开漏输出
  GPIO_Mode_Out_PP = 0x10,   // 推挽式输出
  GPIO_Mode_AF_OD = 0x1C,   // 开漏复用功能
  GPIO_Mode_AF_PP = 0x18   // 推挽式复用功能
}GPIOMode_TypeDef;

初始化GPIO的例程如下

void Drv_Gpio_Init (void)
{
   
   
    GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
    // 开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB,ENABLE);

    // 配置结构体
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置结构体
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

这里初始化的是PA0,PA8和PB1,PB2。在开启时钟不必要写两遍相同的代码用来初始化GPIOA和GPIOB的时钟。写一句,用“ | ”同时开启两个GPIO的时钟。写Pin时也同理。

四、Boot引脚

这里简单介绍一下Boot引脚的配置,对于只是利用核心板编写程序的小伙伴来说Boot引脚的存在感较低,但是当我们需要绘制硬件电路图时,Boot引脚怎么连接就显得很重要。
中文参考手册中介绍如下

1b54d4619a0fd5482cadfc159aa26a5a_342ecd9661f34b40b2f03f16aa8b8921.png

这里简单说一下本人经历得来的经验。如果想使用USB转TTL通过串口下载程序,Boot0需要通过KΩ级电阻接VCC,Boot1接地。下载完程序后再将Boot0接地。需要注意的是这个KΩ级别的电阻最好用10KΩ左右电阻,如果电阻太大会导致下载失败。

由于本人在使用自制板时只使用过串口下载,所以对于用调试器下载需要怎么配置Boot引脚,未测试。

五、一些特殊的GPIO

在使用GPIO时需要注意一些特殊的GPIO,否则你会疑惑,为什么一些引脚的高低电平无法控制。其实有些GPIO在上电后就有自己的默认设置,会稳定在高电平或者低电平。比如用作JTAG的几个GPIO——PB3,PB4,PA13,PA14和PA15。这几个GPIO在上电后就已经默认用作JTAG,即使用上面的GPIO初始化程序将这几个引脚初始化后,依旧无法控制(无法正常用程序拉高拉低)。

这个时候我们需要对这几个GPIO进行复用重映射,关掉其默认的功能,才能作为普通的GPIO使用。针对JTAG/SWD复用功能重映射,中文参考手册描述如下
c1357bee0480563ade52435421685ee4_3415ed0cf73c416a8869be95d0dba77d.png
图中说明这几个GPIO有几种模式,完全SWJ,表中的五个IO均不可作为普通IO使用。完全SWJ但是没有JNTRST,PB3和PB4可用,依此解读。上图是针对寄存器开发描述的内容,使用库函数开发时有封装好的函数,但是需要注意的是需要提前开启AFIO时钟(对于AFIO是指什么,大家可以自行了解)。使用库函数开发时针对特殊引脚进行重映射的操作步骤为

  • 定义GPIO结构体
  • 开启GPIO时钟和AFIO时钟
  • 重映射引脚(根据所需情况设定引脚模式)
  • 配置GPIO结构体初始化GPIO

库函数中提供了可以选择的三种引脚模式

GPIO_Remap_SWJ_NoJTRST          // 完全SWJ(恢复引脚的默认功能)
GPIO_Remap_SWJ_JTAGDisable      // 关闭JTAG,启用SW-DP
GPIO_Remap_SWJ_Disable          // 关闭JTAG-DP,关闭SW-DP

提供了一个函数,可以进行重映射操作

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);

当需要使用到上述的五个GPIO时,初始化程序需要修改,举例如下

void Drv_Gpio_Init (void)
{
   
   
    GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
    // 开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | 
                           RCC_APB2Periph_AFIO,ENABLE);

    // 关闭JTAG-DP,关闭SW-DP
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);

    // 配置结构体
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置结构体
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

STM32F103ZET6芯片GPIO资源较为丰富,平时开发时尽量不要使用这几个特殊引脚。

六、点亮LED

学习完GPIO后我们就可以利用GPIO进行一些简单的操作,比如点亮LED,使用蜂鸣器,用IO驱动小黄电机,检测按键等。

点亮LED比较简单,但是也有一些需要注意的点

  • 根据硬件电路确定LED是高电平点亮还是低电平点亮
  • 初始化GPIO后要先将所有的LED

1. 硬件电路

下图所示电路中,LED右侧接3.3V,左侧接IO口。此时如果想点亮LED,需要将对应的IO电平拉低。相反如果右侧接的是地,如果想要点亮LED,需要将对应IO电平拉高。
3f72d674c5407c061246e6ca54bc4ef1_b367539549ad4dd98effa7364a823aec.png

2. 拉高/拉低GPIO

如何将GPIO电平拉高拉低?库函数提供了封装好的函数

// 设置为高电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

// 设置为低电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

3. 程序设计

点亮LED很简单,只需要初始化相应的GPIO,输入模式设置为推挽式输出,然后设定电平即可。

这里给出点亮LED的例程,LED电路为一侧接3.3V,另一侧接GPIO。

这里给出的只是一些必要的函数,如果需要工程模板可私信联系。

需要注意的是,初始化GPIO时需要开启时钟,开启时钟之前需要确认GPIO挂载的总线。

/*
 *==============================================================================
 *函数名称:Drv_Led_Init 
 *函数功能:初始化LED的GPIO
 *输入参数:无
 *返回值:无
 *==============================================================================
*/
void Drv_Led_Init (void)
{
   
   
    GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
    // 开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

    // 配置结构体
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_1);   // 熄灭LED
}

/*
 *==============================================================================
 *函数名称:Med_Led_On 
 *函数功能:点亮LED
 *输入参数:无
 *返回值:无
 备注:如果有多个LED可以定义一个结构体或者使用swtich,增加一个输入变量
      来确定开启哪个LED
 *==============================================================================
*/
void Med_Led_On (void)
{
   
   
    GPIO_ResetBits(GPIOA,GPIO_Pin_1);   // 熄灭LED
}

七、GPIO的位带操作

对于什么是“位带”这里就不做解释了,大家可以自行搜索。这里只介绍如何使用。位带操作可以使操作GPIO变得更加简单。在模板程序的sys.h文件中已经定义好了各个IO的位带操作所需内容

//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

有了位带操作后,就不需要再使用前面介绍的拉高拉低函数来操作GPIO。比如需要拉d低PA1

PAout(1) = 0;   // 拉低PA1

有了位带操作后也可以对GPIO进行宏定义,用自己想要的名字来操作它。还是拿上面的拉低PA1举例

#define LED   PAout(1)   // 将PA1宏定义为LED

LED = 1;   // 拉高PA1
LED = 0;   // 拉低PA1
相关文章
|
3月前
|
存储
STM32F103标准外设库——GPIO 输入、输出 (五)
STM32F103标准外设库——GPIO 输入、输出 (五)
75 0
STM32F103标准外设库——GPIO 输入、输出 (五)
|
7月前
STM32速成笔记(三)—按键检测
本文介绍了如何利用STM32进行按键检测,先介绍了原理,后面给出了配置步骤和应用例程。此外,本文还叙述了如何利用一个按键单独控制一个LED亮灭,以及如何检测按键长短按。
209 0
STM32速成笔记(三)—按键检测
|
4月前
|
芯片
STM32 GPIO工作原理详解
STM32 GPIO工作原理详解
|
7月前
|
存储 物联网 芯片
STM32速成笔记(十四)—串口IAP
本文介绍了什么是IAP,IAP有什么作用,如何实现IAP。最后,给出了IAP的实现程序。
115 0
STM32速成笔记(十四)—串口IAP
|
7月前
|
芯片 内存技术
STM32速成笔记(十三)—低功耗模式
本文介绍了三种STM32低功耗模式的进入和退出方法,针对待机唤醒给出了程序设计。
145 0
STM32速成笔记(十三)—低功耗模式
|
7月前
|
存储 芯片 内存技术
STM32速成笔记(十二)—Flash闪存
本文简单介绍了什么是Flash。针对STM32F1的Flash做了详细介绍,介绍了操作Flash的步骤,并且给出了程序设计。最后,介绍了一些注意事项。
44 0
STM32速成笔记(十二)—Flash闪存
|
7月前
|
存储 芯片
STM32速成笔记(十一)—EEPROM(AT24C02)
本文详细介绍了什么是AT24C02,介绍了它的引脚,读/写时序,给出了应用实例和详细的程序设计。最后,简单介绍了AT24C02的应用场景。
158 0
STM32速成笔记(十一)—EEPROM(AT24C02)
|
7月前
STM32速成笔记(十)—IWDG
本文详细介绍了什么是IWDG,STM32的IWDG特性,框图和配置步骤。此外,给出了STM32的IWDG配置程序。通过一个简单的应用实例,展示了IWDG的配置和使用方法。
65 0
STM32速成笔记(十)—IWDG
|
7月前
|
API
STM32速成笔记(九)—RTC
本文详细介绍了RTC模块,介绍了STM32的RTC的特性,框图,配置步骤,并给出了详细的程序设计。最后,针对实际使用时可能遇到的问题给出了解决方法以及程序。
54 0
STM32速成笔记(九)—RTC
|
7月前
|
存储 Perl
STM32速成笔记(八)—DMA
本文介绍了DMA的概念,用途。对于STM32F103ZET6的DMA做出了详细地介绍,给出了DMA配置步骤。最后,以配置DMA搬运ADC转换结果为例,给出了DMA的配置和使用方法。
100 0
STM32速成笔记(八)—DMA