GPIO输出

GPIO简介

  • GPIO:通用输入输出
  • 引脚电平:0V~3.3V,部分引脚可容忍5V
  • 输出模式下可控制端口输出高低点评,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
  • 输入模式下可读取端口的高低点评或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等。
  • image-20230311103301363

    1. 保护二极管

      引脚内部加上这两个保护二级管可以防止引脚外部过高或过低的电压输入,当引脚电压高于VDD_FT或VDD时,上方的二极管导通吸收这个高电压,当引脚电压低于VSS时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。

      尽管STM32芯片内部有这样的保护,但并不意味着STM32的引脚就无所不能,如果直接将引脚连接大功率器件,比如电机,那么要么电机不转,要么烧坏芯片。如果要驱动一些大功率器件,必须要加大功率及隔离电路驱动。也可以说STM32引脚是用来做控制,而不是做驱动使用的。

    2. 上下拉电阻

      从图中可以看到,上拉和下拉电阻上都有一个开关,通过配置上下拉电阻开关,可以控制引脚的默认状态电平。当开启上拉时引脚默认电压为高电平,开启下拉时,引脚默认电压为低电平,这样就可以消除引脚不定状态的影响。当然也可以将上拉和下拉的开关都关断,这种状态我们称为浮空模式,一旦配置成这个模式,引脚的电压是不确定的,如果用万用表测量此模式下管脚电压时会发现只有 1 点几伏,而且还不时改变,所以一般情况下我们都会给引脚设置成上拉或者下拉模式,使它有一个默认状态。STM32 上下拉及浮空模式的配置是通过GPIOx_CRL和GPIOx_CRH寄存器控制的,大家在《STM32F1xx中文参考手册》查阅。STM32内部的上拉其实是一个弱上拉,也就是说通过此上拉电阻输出的电流很小,如果想要输出一个大电流,那么就需要外接上拉电阻了。

    3. P-MOS和N-MOS管

      GPIO引脚经过两个保护二极管后就分成两路,上面一路是“输入模式”,下面一路是“输出模式”。我们先讲输出模式,线路经过一个由P-MOS和N-MOS管组成的单元电路,这让GPIO引脚具有了推挽和开漏两种输出模式。所谓推挽输出模式,是根据P-MOS和N-MOS管的工作方式命名的。在该结构单元输入一个高电平时,P-MOS管导通,N-MOS管截止(可以将P-MOS当作NPN三极管,N-MOS当作PNP三极管来看就非常清楚),对外输出高电平(3.3V)。

      在该单元输入一个低电平时,P-MOS管截止,N-MOS管导通, 对外输出低电平 (0V) 。如果当切换输入高低电平时,两个MOS管将轮流导通,一个负责灌电流(电流输出到负载),一个负责拉电流(负载电流流向芯片),使其负载能力和开关速度都比普通的方式有很大的提高。下图为推挽输出模式的等效电路。简单记就可以理解为推挽模式下,输出高低电平都由他说了算。

      image-20230311134553598

      在开漏输出模式时,不论输入是高电平还是低电平,P-MOS 管总处于关闭状态。当给这个单元电路输入低电平时,N-MOS 管导通,输出即为低电平。当输入高电平时,N-MOS 管截止,这个时候引脚状态既不是高电平,又不是低电平,我们称之为高阻态。如果想让引脚输出高电平,那么引脚必须外接一个上拉电阻,由上拉电阻提供高电平。开漏输出模式等效电路图如下图所示。

      image-20230311134608553

      在开漏输出模式中还有一个特点,引脚具有“线与”关系。就是说如果有很多个开漏输出模式的引脚接在一起,只要有一个引脚为低电平,其他所有管脚都为低,即把所有引脚连接在一起的这条总线拉低了。只有当所有引脚输出高阻态时这条总线的电平才由上拉电阻的 VDD 决定。如果 VDD 连接的是 3.3V,那么引脚输出的就是 3.3V,如果 VDD 连接的是 5V,那么引脚输出的就是 5V。因此如果想要让 STM32 管脚输出 5V,可以选择开漏输出模式,然后在外接上拉电阻的电源 VDD 选择 5V 即可,前提是这个 STM32 引脚是容忍 5V 的。开漏输出模式一般应用在 I2C、SMBUS 通讯等需要“线与”功能的总线电路中。还可以用在电平不匹配的场合中,就如上面说的输出 5V 一样。

      推挽输出模式一般应用在输出电平为0-3.3V而且需要高速切换开关状态的场合。除了必须要用开漏输出模式的场合,我们一般选择推挽输出模式。要配置引脚是开漏输出还是推挽输出模式可以使用GPIOx_CRL和GPIOx_CRH寄存器, 寄存器详细内容可以参考《STM32F1xx 中文参考手册》“通用和复用I/O(GPIO和AFIO)”章节。

    4. 输出数据寄存器

      前面提到的双 MOS 管结构电路的输入信号,是由 GPIO“输出数据寄存器GPIOx_ODR”提供的, 因此我们通过修改输出数据寄存器的值就可以修改GPIO引脚的输出电平。而“置位/复位寄存器 GPIOx_BSRR”可以通过修改输出数据寄存器的值从而影响电路的输出。

    GPIO的8种模式

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

    image-20230311141153917

    1. 浮空/上拉/下拉/输入模式

      image-20230312195413518

      上拉:引脚内部有一个上拉电阻,通过开关连接到电源VDD,当I/O引脚无输入信号时,默认为高电平;其典型应用就是接外部按键,当没有按下时,默认为高电平,按下则为低电平。

      下拉:与上拉输入模式正好相反。

      浮空:内部即不接上拉也不下拉,直接经施密特触发器或不处理进入片上外设。

    2. 模拟输入模式

      image-20230312195922683

      该模式下,只有一个施密特触发器是中间处理,但出于关闭状态,信号直接连接到片上外设,典型应用是A/D模式输入,实现对外部信号采集,类似采集电信号的频率周期等。

    3. 开漏/推输出模式

      解析见上边P-MOS与N-MOS介绍

    4. 复用开漏/推输出模式

      image-20230312201210367

      复用功能输出模式接片上外设,当I/O引脚用作复用功能时,可选择复用推挽输出或复用开漏输出,选择复用开漏输出时,需外接上拉电阻。

    模式选择与端口初始化

    在进行输出操作之前,需要对GPIO引脚进行初始化

    需先实例化GPIO初始化对象

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口 可以或运算是因为配置选择寄存器时是按位选择哪一个引脚的 也可以GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
    

    在进行按位置位或者清空时,可以用

    GPIO_SetBits(GPIOA,GPIO_Pin_9 | GPIO_Pin_10);

    GPIO_ResetBits(GPIOA,GPIO_Pin_9 | GPIO_Pin_10);

    下面是通过 标准库函数、位带、直接操作寄存器方式实现流水灯:

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    
    
    int main(void)
    { 
     
    	delay_init(168);		  //初始化延时函数
    	LED_Init();		        //初始化LED端口
    	
      /**下面是通过直接操作库函数的方式实现IO控制**/	
    	
    	while(1)
    	{
    	GPIO_ResetBits(GPIOF,GPIO_Pin_9);  //LED0对应引脚GPIOF.9拉低,亮  等同LED0=0;
    	GPIO_SetBits(GPIOF,GPIO_Pin_10);   //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
    	delay_ms(500);  		   //延时300ms
    	GPIO_SetBits(GPIOF,GPIO_Pin_9);	   //LED0对应引脚GPIOF.0拉高,灭  等同LED0=1;
    	GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
    	delay_ms(500);                     //延时300ms
    	}
    }
    
    
    /**
    *******************下面注释掉的代码是通过 位带 操作实现IO口控制**************************************
    	
    int main(void)
    { 
     
    	delay_init(168);		  //初始化延时函数
    	LED_Init();		        //初始化LED端口
      while(1)
    	{
         LED0=0;			  //LED0亮
    	   LED1=1;				//LED1灭
    		 delay_ms(500);
    		 LED0=1;				//LED0灭
    		 LED1=0;				//LED1亮
    		 delay_ms(500);
    	 }
    }
    **************************************************************************************************
     **/	
    	
    /**
    *******************下面注释掉的代码是通过 直接操作寄存器 方式实现IO口控制**************************************
    int main(void)
    { 
     
    	delay_init(168);		  //初始化延时函数
    	LED_Init();		        //初始化LED端口
    	while(1)
    	{
         GPIOF->BSRRH=GPIO_Pin_9;//LED0亮
    	   GPIOF->BSRRL=GPIO_Pin_10;//LED1灭
    		 delay_ms(500);
         GPIOF->BSRRL=GPIO_Pin_9;//LED0灭
    	   GPIOF->BSRRH=GPIO_Pin_10;//LED1亮
    		 delay_ms(500);
    
    	 }
     }	 
    **************************************************************************************************
    **/	
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » GPIO输入与GPIO输出

    发表评论