三、GPIO

3.1 GPIO介绍

3.1.1 GPIO输出
  • GPIO(Gneral Purpose Input Output)通用输入输出口(I/O口)

  • 根据使用场景可配置为8种输入输出模式

  • 引脚电平:0V~3.3V,部分引脚可容忍5V(对于输入而言,但是输出最大是3.3V,因为供电是3.3V)

  • 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议(I2C、SPI)输出时序等,如果是功率比较大的设备,只需要再加入驱动电路即可

  • 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入(光敏、热敏电阻)、ADC电压采集、模拟通信协议接收数据等

3.1.2 GPIO基本结构

在STM32中,所有的GPIO都是挂载在APB2外设总线上的。

命名规则是:GPIOA、GPIOB、GPIOC……

每个GPIO外设,总共有16个引脚,编号是从0到15,PA0、PA1……PA15

内核可以通过APB2总线对寄存器(32位)进行读写,以完成输出电平和读取电平的功能,驱动器可以增大驱动能力

3.1.3 GPIO位结构(电路如何工作)

左边部分是寄存器,中间是驱动器,右边是某一个IO口的引脚,整体可以分为两个部分,上面是输入部分,下面是输出部分

输入部分

右边接两个保护二极管,对输入电压进行限幅,上方连VDD(芯片供电正极) 3.3V,下方接VSS(芯片供电负极) 0V,保证电压在这个范围内

两个开关可以控制上拉和下拉输入模式,上面导通就是上拉,下面导通就是下拉,如果都断开就是浮空输入模式。接入上拉电阻,当引脚悬空时们还有上拉电阻来保证引脚的高电平,所以上拉输入又可以称作是默认为高电平的输入模式,下拉同理,默认为低电平的输入模式。

施密特触发器:如果输入电压大于某一阈值,输出就会瞬间升为高电平,如果输入电压小于某一阈值,输出就会瞬间降为低电平,其余状态保持不变;数字信号在输入的时候会产生各种失真,该触发器可以有效避免因信号波动造成的输出抖动现象

红色为失真的数字信号,蓝色为经过施密特触发器的信号

经过触发器的信号直接进入输入数据寄存器,上面两条线路是连接到片上外设的一些端口

输出部分

数字部分可以由输出数据寄存器或片上外设控制,两种控制方式通过数据选择器接到了输出控制部分

位设置/清除寄存器:可以用来单独操作输出数据寄存器的某一位,而不影响其他位,因为输出数据寄存器同时控制16个端口,并且寄存器只能整体读写,如果想单独控制需要别的方式,第一种方式是先读出这个寄存器,然后用按位与和按位或的方式更改某一位,然后再将更改后的数据写回去,但是效率不高。第二种方式是操作该寄存器,如果要对某一位进行置1的操作,在位设置寄存器的对应位写1即可,剩下不需要的位写0,如果想置0,就在位清楚寄存器的对应位写1即可,注意!!!不管是置1还是0都是在不同寄存器位置上写1。

两个MOS管:电子开关,通过代码控制,可以选择推挽(强推模式)、开漏或关闭三种输出方式

推挽输出模式下,P-MOS和N-MOS均有效,数据寄存器为1时,上管导通,下管断开,输出直接接到VDD,输出高电平,为0时,上管断开,下管导通,输出直接接到VSS,就是输出低电平

开漏输出模式下,数据寄存器为1,下管断开,输出相当于断开也就是高阻模式,为0时,下管导通,输出直接接到VSS,也就是输出低电平,这个模式只有低电平有驱动能力作为通信协议的驱动方式I2C通信的引脚,就是使用的开漏模式,在多机通信的情况下,这个模式可以避免各个设备互相干扰。开漏模式还可以用于输出5V的电平信号,在IO口外接一个上拉电阻到5V的电源,当输出高点平时,由外部的上拉电阻拉高至5V

关闭输出模式下,两个管都无效,输出关闭,端口电平由外部信号来控制

3.1.4 GPIO模式

通过配置GPIO的端口配置寄存器,端口可以配置成以下8种模式

在使用浮空输入时,一定要接上一个连续的驱动源,不能出现悬空的状态

输入模式下,输出驱动是断开的,端口只能输入不能输出。

模拟输入时,输入和施密特触发器都是断开的,直接接入片上外设(ADC),因此,当使用ADC(模数转换器)的时候,将引脚配置为模拟输入就行。

开漏&推挽输出模式,区别在与开漏模式的高电平是高阻态模式,没有驱动能力

注意!!!:配置成输出模式的时候,输入模式依旧是打通的,可以进行输入

复用开漏&推挽输出:信号来源于片上外设,其他和普通的没有区别

3.1.5 LED&蜂鸣器介绍

LED:发光二极管,正向通电点亮,反向通电不点亮;内部较小的一半是正极,较大的一半是负极。

电路:左边正极,右边负极

有源蜂鸣器:自带振荡源,正负极接上直流电压即可持续发生,且频率固定

无源蜂鸣器:需要控制提供脉冲

LED&蜂鸣器电路

左边是LED,上面是低电平驱动(单片机一般使用),上面是高电平驱动,电阻可以调整LED亮度同时防止烧毁。

右边是蜂鸣器,采用了三极管,三极管左边是基级,带箭头的发射级,剩下的集电极,上面PNP三极管,基极给低电平,三极管就会导通,就可以驱动蜂鸣器,下面的NPN三极管逻辑相反,PNP蜂鸣器需要在下面,因为激活三极管需要基极和发射级之间有一定的电压,NPN相同

3.1.6 面包板的使用

上下两条是接电源,如果需要供电,就从上下的孔位中,用条线引出来即可,不是所有面包板上下两排都是连接的,那就需要用跳线连接到一起

点亮LED的链接方式,纵向的孔可以理解为直接相连的

3.2 点亮LED小灯实战

本节会用到RCC和GPIO两个功能

3.2.1 RCC库函数

寻找库函数:

void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);

(代码来源于keil中库函数的声明)

RCC常用的三个库函数如上

AHB外设时钟控制函数(第一个):使能或者失能AHB外设时钟

第一个参数为选择哪一个外设,STM互联型的设备在下面的arg中可以进行选择,其他设备在下面选择。

第二个参数为enable和disable也就是开启或者关闭

APB1和ABP2是一样的参数

如果不清楚哪个外设是连接在连接在哪个总线上,还可以通过列表进行查找。

3.2.2 GPIO库函数
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

(代码来源于keil中库函数的声明)

GPIO_DeInit:用于复位指定的GPIO,调户函数之后,指定的GPIO会被复位

void GPIO_AFIODeInit:可以复位AFIO外设

void GPIO_Init:用结构体的参数来初始化GPIO口,需要先定义一个结构体变量,然后再给结构体赋值(赋的值是比如IO口是输入还是输出,推挽还是开漏),最后调用这个函数,这个函数内部会自动读取结构体的值,然后自动把外设的各个参数配置好。(这种Init函数在STM31中基本所有的外设都有)

void GPIO_StructInit:把结构体变量赋一个默认值

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx); uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

以上四个是GPIO的读取函数

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal); void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

以上四个是写入函数

通过以上常用函数就可以实现GPIO的读写功能

3.2.3 点亮小灯

1、激活时钟,选择RCC_APB2Periph_GPIOA

原因:AHB是用于激活高速核心外设,例如flash存储器,cortex内核,GPIO是高速外设因此挂在ABP2上,因为LED硬件接在PA0口,因此选择GPIOA

2、初始化GPIO,此时需要定义结构体,但是库函数已经帮我找好了三个参数Mode、Pin和Speed,具体代码见下方完整代码

Mode:单机main函数中的Mode跳转,选中Mode_Define,ctrl+F搜索

最终跳转页面如上图,此处是GPIO的八种工作模式

AIN:模拟输入

IN_:浮空输入

IPD:下拉输入

IPU:上拉输入

Out_OD:开漏输出;AF复用

Out_PP:推挽输出;AF复用

此处点亮小灯使用推挽输出,选择Out_PP这一项

Pin:选择引脚

右键跳转后Pin有多个选项,此处选择member选项,然后同上Mode选择即可

此处选择P0

Speed处选择50MHz即可

此处初始化第二个参数完成,注意第二个参数需要加&(取地址符)

3、使用四个写入函数

void GPIO_SetBits:把指定端口设置为高电平

void GPIO_ResetBits:把指定端口设置为低电平

以上两个函数的参数都是用于寻找端口

void GPIO_WriteBit:根据第三个参数的值来设置指定的端口

void GPIO_Write:第二个参数可以同时对十六个端口进行写入

因为单片机一般使用低电平驱动,因此使用WriteByte函数时,需要设为RESET

此时就已经完成了LED点亮的功能,代码如下

#include "stm32f10x.h"                  // Device header
​
int main()
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; 
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
    
    while(1)
    {
        
    }
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐