Tangwx

Tangwx

博客网站

03.STM32GPIO通用输入输出口

3 GPIO 通用输入输出口#

3.1 GPIO 简介#

GPIO(General Purpose Input Output)通用输入输出口

可配置为 8 种输入输出模式

引脚电平:0V~3.3V,部分引脚可容忍 5V

输出模式下可控制端口输出高低电平,用以驱动 LED、控制蜂鸣器、模拟通信协议输出时序等

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

3.2 GPIO 基本结构#

在 STM32 中,所有的 GPIO 都是挂载在 APB2 外设总线上的,其中 GPIO 的名称是按照 GPIOA、GPIOB、GPIOC 等来命名的,每个 GPIO 外设都有 16 个引脚,编号是从 0 到 15,再每个 GPIO 模块内,主要包含了寄存器和驱动器这些东西,寄存器就是一段特殊的存储器,内核可以通过 APB2 总线对寄存器进行读写,这样就可以完成输出电平和读取电平的功能了。这个寄存器的每一位对应一个引脚,其中输出寄存器写 1,对应的引脚就会输出高电平,写 0 就会输出低电平。输入寄存器读取为 1 说明对应的端口目前是高电平,读取为 0 说明是低电平。因为 STM32 是 32 位的单片机,所以 STM32 内部的寄存器都是 32 位的,但端口只有 16 位,所以这个寄存器只有低 16 位对应的有端口,高 16 位没有用到。这个驱动器是用来增加信号的驱动能力的,寄存器只负责存储数据,如果要进行点灯这样的操作的话,还是需要驱动器来负责增大驱动能力,这些就是 GPIO 的整体基本结构了。

image-20220821193133744

3.3 GPIO 位结构#

GPIO 中每一位的具体电路结构,下图为 STM32 参考手册中的 GPIO 为结构的电路图,左边 3 个是寄存器,中间这部分是驱动器,右边是某一个 IO 口的引脚。整体结构可以分为两部分,上半部分为输入部分,下半部分为输出部分。

3.3.1 输入部分#

引脚处接了两个保护二极管,对输入电压进行限幅,上面二极管接 VDD-3.3V,下面接 VSS-0V。若输入电压比 3.3V 高,则上方这个二极管导通,输入电压产生的电流就会直接流入 VDD 而不流入内部电路,这样就可以避免过高的电压对内部这些电路产生伤害。若输入电压比 0V 低,这个电压是相对于 VSS 的电压,所以是可以有负电压的,此时下方这个二极管导通,电流从 VSS 直接流出去,不会从内部电路汲取电流,这样也是可以保护内部电路的。若输入的电压在 0~3.3V 之间,此时两个二极管均不导通,二极管对电路没有影响,这就是保护二极管的用途。电流经过上拉电阻和下拉电阻,上拉电阻至 VDD,下拉电阻至 VSS,这个开关是可以通过程序进行配置的,如果上面导通、下面断开,就是上拉输入模式;如果下面导通、上面断开,就是下拉输入模式;如果两个都断开,就是浮空输入模式。上拉和下拉是为了给输入提供一个默认的输入电平,对于一个数字端口,输入不是高电平就是低电平, 如果输入啥都不接,这时输入就会处于一种浮空状态,引脚的输入电平极易受外界干扰而改变, 所以为了避免引脚悬空导致的输入数据不确定,我们就需要在这里加上上拉或下拉电阻。若接入上拉电阻,当引脚悬空时还有上拉电阻来保证引脚的高电平,所以上拉输入又可以称为默认高电平的输入模式;下拉也是同理,默认为低电平的输入方式。上拉电阻和下拉电阻的阻值都是比较大的,是一种弱上拉和弱下拉,目的是尽量不影响正常的输入操作。TTL 肖特基触发器这里应该是施密特触发器,作用是对输入电压进行整形,他的执行逻辑是,当输入电压大于某一阈值,输出就会瞬间升为高电平,当输入电压小于某一阈值,输出就会瞬间降为低电平。对于施密特触发器来说,只有高于上限或低于下限输出才会变化。经过施密特触发器整型的波形就可以直接写入数据寄存器内,我们再用程序读取输入数据寄存器对应某一位的数据,就可以知道端口的输入电平了。模拟输入是连接到 ADC 上的,因为 AD 需要接收模拟量,所以这根线需要接到施密特触发器前面的,另一个是复用功能输入,这个是连接到其他需要读取端口外设上的,例如串口的输入引脚等,这根线接收的是数字量,所以跟在施密特触发器后面

3.3.2 输出部分#

数字部分由输出数据寄存器或片上外设控制,通过数据选择器接到输出控制部分,如果选择通过输出数据寄存器进行控制,就是普通的 IO 口输出,写这个数据寄存器的某一位就可以操作对应的某个端口。左边还有一个位设置 / 清除寄存器,用来单独操作输出数据寄存器的某一位而不影响其他位,这个输出数据寄存器同时控制 16 个端口,并且这个寄存器只能整体读写,若想单独控制其中某一个端口而不影响其他端口的话,需要一些特殊的操作方式。第一种操作方式是先读出这个寄存器,然后用按位与&=和按位或|=的方式来更改某一位,最后再将更改后的数据写回去。这种方法比较麻烦且效率不高,对于 IO 口的操作而言不太合适。第二种方式就是通过设置这个位设置 / 清除寄存器,如果我们要对某一位进行置一的操作,在位设置寄存器的对应位写一即可,剩下不需要操作的位写 0,这样它内部就会有电路自动将输出数据寄存器中对应位置为 1,其他写 0 的位置保持不变。这样就保证了只操作其中一位而不影响其它位,并且这是一步到位的操作。如果想对某一位进行清零的操作,就在位清除寄存器的对应位写 1 即可,这样内部电路就会把这一位清零了。另外还有第三种操作方式,就是读写 STM32 中的位带区域,这个位带的作用就跟 51 单片机的位寻址作用差不多,在 STM32 中,专门分配的有一段地址区域,这段地址映射了 RAM 和外设寄存器的所有位,读写这段地址中的数据就相当于读写所映射位置的某一位,这就是位带的操作方式,这个方式我们本课程暂不使用,我们主要使用库函数来操作,库函数使用的就是读写位设置 / 清除寄存器的方法。输出控制之后就接到了两个 MOS 管,上面是P-MOS,下面是N-MOS,这个 MOS 管就是一种电子开关,我们的信号来控制开关的导通和关闭,开关负责将 IO 口接到VDDVSS,在这里可以选择推挽、开漏或者关闭三种输出方式。在推挽输出下,P-MOSN-MOS均有效,数据寄存器为 1 时,上管导通,下管断开,输出直接接到 VDD,就是输出高电平; 数据寄存器为 0 时,上管断开,下管导通,输出直接接到 VSS,就是输出低电平。这个模式下,高低电平均有较强的驱动能力,所以推挽输出模式也可以叫强推输出模式。在推挽输出模式下,STM32 对 IO 口具有绝对的控制权,高低电平都由 STM32 说了算。在开漏输出模式下,P-MOS无效,只有N-MOS在工作,数据寄存器为 1 时,下管断开,此时输出相当于断开,也就是高阻模式;数据寄存器为 0 时,下管导通,输出直接接到 VSS,输出低电平。这种模式下,只有低电平有驱动能力,高电平是没有驱动能力的。开漏模式可以作为通信协议的驱动方式,比如 I2C 通信的引脚就是使用的开漏模式,在多机通信的情况下,这个模式可以避免各个设备的相互干扰,此外开漏模式还可以用于输出 5V 的电平信号,在 IO 口外接一个上拉到 5V 的电源,当输出低电平时,内部的 N-MOS 直接接 VSS,当输出高电平时,由外部的上拉电阻拉高至 5V,这样就可以输出 5V 的电平信号,用于兼容一些 5V 电平的设备,这就是开漏输出的主要用途。剩下的一种状态是关闭,这是当引脚配置成输入模式的时候,这两个 MOS 管都无效,也就是输出关闭,端口的电平由外部信号来控制,以上就是 GPIO 位结构的全部介绍。

image-20220821193240193

3.4 GPIO 的 8 种工作模式#

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

浮空输入、上拉输入、下拉输入的电路结构基本是一样的,区别就是上拉电阻和下拉电阻的连接,他们都属于数字输入口,都可以读取端口高低电平。当引脚悬空时,上拉输入默认是高电平,下拉输入默认是低电平,而浮空输入的电平是不确定的,所以在使用浮空输入时,端口一定要接上一个连续的驱动源,不能出现悬空的状态。

模拟输入特征是 GPIO 无效,引脚直接接入内部 ADC,是 ADC 模数转换器的专属配置

开漏输出与推挽输出这两个电路的结构也基本一样,都是数字输出端口,用于输出高低电平,区别就是开漏输出的高电平呈现的是高阻态,没有驱动能力,而推挽输出的高低电平都是具有驱动能力的。

复用开漏输出和复用推挽输出与普通的开漏输出和推挽输出差不多,是复用的输出,引脚电平由片上外设控制。

在 GPIO 的这 8 种模式中,除了模拟输入这个模式会关闭数字的输入功能,在其他 7 个模式中输入都是有效的

模式名称** 性 质 **特征
浮空输入数字输入可读取引脚电平,若引脚悬空,则电平不确定
上拉输入数字输入可读取引脚电平,内部连接上拉电阻,悬空时默认高电平
下拉输入数字输入可读取引脚电平,内部连接下拉电阻,悬空时默认低电平
模拟输入模拟输入GPIO 无效,引脚直接接入内部 ADC
开漏输出数字输出可输出引脚电平,高电平为高阻态,低电平接 VSS
推挽输出数字输出可输出引脚电平,高电平接 VDD,低电平接 VSS
复用开漏输出数字输出由片上外设控制,高电平为高阻态,低电平接 VSS
复用推挽输出数字输出由片上外设控制,高电平接 VDD,低电平接 VSS

3.5 GPIO 输入浮空 / 上拉 / 下拉配置#

在输入模式下,输出驱动器是断开的,端口只能输入而不能输出。上面两个电阻可以选择为上拉工作或下拉工作或不工作,对应是上拉输入、下拉输入和浮空输入。输入通过施密特触发器进行波形整型后,连接到输入数据寄存器。右边输入保护上面写的是 VDD 或 VDD_FT,这是 3.3V 端口和 5V 端口的区别。容忍 5V 的引脚它的上边保护二极管要做一下处理,不然这里直接接 VDD3.3V 的话,外部再接入 5V 电压就会导致上边二极管开启,并且产生较大的电流。

image-20220821193727313

3.6 GPIO 高阻抗的模拟输入配置#

模拟输入的输出也是断开的,输入的施密特触发器也是关闭无效的状态,GPIO 的绝大部分都是没用的,只剩下从引脚直接接入片上外设,也就是 ADC,因此当我们使用 ADC 的时候,将引脚配置为模拟输入即可,其他时候一般用不到模拟输入。

image-20220821193914682

3.7 GPIO 开漏 / 推挽输出配置#

输出由输出数据寄存器控制,P-MOS如果无效,就是开漏输出,如果P-MOSN-MOS都有效,就是推挽输出。另外在输出模式下,输入模式也是有效的。一个端口只能有一个输出,但可以有多个输入,当配置成输出模式的时候,内部也可以顺便输入一下。

image-20220821193955149

3.8 复用开漏 / 推挽输出#

输出控制左边通用的输出这里是断开的,引脚的控制权转移到了片上外设,由片上外设来控制,在输入部分,片上外设也可以读取引脚的电平,同时普通的输入也是有效的,顺便接收一下电平信号。

image-20220829075654841

3.9 GPIO 控制 LED 和有源蜂鸣器#

image-20220829084106943

USE使用 STD标准 PERIPH外设 DRIVER驱动

3.9.1 LED 闪烁#

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//RCC开启GPIO时钟 打开C口时钟并使能
	GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体变量
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//通用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;//PC13口
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz速度
	GPIO_Init(GPIOC, &GPIO_InitStructure);//GPIO_Init初始化GPIO 配置端口模式
    //输出或输出 SetBits设置为高电平 ResetBits设置为低电平
//	GPIO_SetBits(GPIOC, GPIO_Pin_13);
	GPIO_ResetBits(GPIOC, GPIO_Pin_13);
	while (1)
	{
        GPIO_SetBits(GPIOC, GPIO_Pin_13);//SetBits设置为高电平
        Delay_ms(500);
		GPIO_ResetBits(GPIOC, GPIO_Pin_13); //ResetBits设置为低电平
        Delay_ms(500);
        
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//低电平
		Delay_ms(500);
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//高电平
		Delay_ms(500);
        
        GPIO_WriteBit(GPIOC,GPIO_Pin_13,{BitAction}0);//低电平
		Delay_ms(500);
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,{BitAction}1);//高电平
		Delay_ms(500);
	}
}

3.9.2 LED 流水灯#

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/************************************************************************************************************************
* GPIO(General Purpose Input Output)通用输入输出口
* 可配置为8种输入输出模式
* 引脚电平:0V~3.3V,部分引脚可容忍5V FT的
* 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
* 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等
* GPIO端口图片里的ttl肖特基触发器应该是施密特触发器
* 施密特触发器只有在高于上限或低于下限电平才会变化,很好的消抖
* 推挽输出 开漏输出 关闭 三种状态
* 推挽输出 P-MOS 和 N-MOS均有效 这种模式下,高低电平均有较强的驱动能力,所以推挽输出又叫强推输出模式
* 在推挽输出模式下,STM32对IO口具有绝对的控制权,高低电平都由STM32说了算
* 开漏输出 P-MOS输出是无效的,只有N-MOS在工作 这种模式下,只有低电平有驱动能力.高电平是没有驱动能力的
* 开漏模式可以作为通信协议的驱动方式,如I2C通信的引脚就是使用的开漏模式,多机通信下这个模式可以避免多个设备相互干扰
* 另外开漏模式还可以用于输出5V的电平信号,
* 关闭 当引脚配置为输入模式时,P-MOS和N-MOS都无效,输出关闭,端口的电平信号都由外部信号来控制
************************************************************************************************************************/

/************************************************************************************************************************
* | **模式名称** | **性质** | ************************特征***********************|
* | ------------ | -------- | -------------------------------------------------- |
* | 浮空输入     | 数字输入 | 可读取引脚电平,若引脚悬空,则电平不确定           |
* | 上拉输入     | 数字输入 | 可读取引脚电平,内部连接上拉电阻,悬空时默认高电平 |
* | 下拉输入     | 数字输入 | 可读取引脚电平,内部连接下拉电阻,悬空时默认低电平 |
* | 模拟输入     | 模拟输入 | GPIO无效,引脚直接接入内部ADC                      |
* | 开漏输出     | 数字输出 | 可输出引脚电平,高电平为高阻态,低电平接VSS        |
* | 推挽输出     | 数字输出 | 可输出引脚电平,高电平接VDD,低电平接VSS           |
* | 复用开漏输出 | 数字输出 | 由片上外设控制,高电平为高阻态,低电平接VSS        |
* | 复用推挽输出 | 数字输出 | 由片上外设控制,高电平接VDD,低电平接VSS           |
* 浮空输入 上拉输入 下拉输入 这三种模式的电路结构基本一致,区别是上下拉电阻的连接
* 开漏输出 推挽输出 这两种电路结构也基本一致 区别在于开漏输出的高电平呈现的是高阻态,没有驱动能力
* 复用开漏输出 复用推挽输出 这两种模式和普通的差不多,只不过复用的输出,引脚电平是由片上外设控制的
* 只有模拟输入会关闭数字的输入功能,其他模式数字输入都是有效的
************************************************************************************************************************/ 


int main(void)
{
	/***********************************************************************************
		GPIO输出
	***********************************************************************************/
	uint16_t i=0;//定义循环变量
	//开启对应时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //打开A口时钟并使能
	//定义一个结构体
	GPIO_InitTypeDef GPIO_InitStructure;
	//配置端口模式
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出
	/**********************************************************
	GPIO8种工作模式
	GPIO_Mode_AIN 模拟输入(analog in)
	GPIO_Mode_IN_FLOATING 浮空输入(in floating)
	GPIO_Mode_IPD 下拉输入(in pull down)
	GPIO_Mode_IPU 上拉输入(in pull up)
	GPIO_Mode_Out_OD 开漏输出(out open drain)
	GPIO_Mode_Out_PP 推挽输出(out push pull)
	GPIO_Mode_AF_OD 复用开漏输出(alt open drain)
	GPIO_Mode_AF_PP 复用推挽输出(alt push pull)
	**********************************************************/
	//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // A4号引脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //A口所有引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHz速度
	//端口初始化
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//设置端口高低电平
	/********************************************************************
	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);//BitAction BitVal值为Bit_RESET和Bit_SET
	void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);//PortVal:指定要写入端口输出数据寄存器的值 
	*********************************************************************/
	//GPIO_SetBits(GPIOC, GPIO_Pin_13);//高电平
	//GPIO_ResetBits(GPIOC, GPIO_Pin_13);//低电平
	//GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);//低电平
	//GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);//高电平
	//GPIO_Write(GPIOC, 0x0001); //0000 0000 0000 0001 对应PC0~PC15共16个引脚
	while(1)
	{
		/*
		//LED闪烁
		GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);//低电平
		Delay_ms(500);
		GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_SET);//高电平
		Delay_ms(500);
		*/
		//流水灯
		for(i=0;i<16;i++)
		{
			GPIO_Write(GPIOA, ~0x0001);//0000 0000 0000 0001 按位取反 低电平点亮
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0002);//0000 0000 0000 0010
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0004);//0000 0000 0000 0100
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0008);//0000 0000 0000 1000
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0010);//0000 0000 0001 0000
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0020);//0000 0000 0010 0000
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0040);//0000 0000 0100 0000
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0080);//0000 0000 1000 0000
			Delay_ms(500);
			//GPIO_Write(GPIOA, 0x0001);//高电平亮
			//Delay_ms(500);
		}
	}

}

3.9.3 蜂鸣器#

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//通用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PB12口
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz速度
	GPIO_Init(GPIOB, &GPIO_InitStructure);//GPIO_Init初始化GPIO 配置端口模式

	while (1)
	{
        GPIO_SetBits(GPIOB, GPIO_Pin_12);//SetBits设置为高电平
        Delay_ms(500);
		GPIO_ResetBits(GPIOB, GPIO_Pin_12); //ResetBits设置为低电平
        Delay_ms(500);
	}
}

3.10 GPIO 输入#

按键抖动

image-20220829223028287

3.10.1 按键控制 LED#

快捷键Ctrl+ALT+空格显示代码提示

main.c#
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"

/************************************************************************************************************************
* GPIO(General Purpose Input Output)通用输入输出口
* 可配置为8种输入输出模式
* 引脚电平:0V~3.3V,部分引脚可容忍5V FT的
* 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
* 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等
* GPIO端口图片里的ttl肖特基触发器应该是施密特触发器
* 施密特触发器只有在高于上限或低于下限电平才会变化,很好的消抖
* 推挽输出 开漏输出 关闭 三种状态
* 推挽输出 P-MOS 和 N-MOS均有效 这种模式下,高低电平均有较强的驱动能力,所以推挽输出又叫强推输出模式
* 在推挽输出模式下,STM32对IO口具有绝对的控制权,高低电平都由STM32说了算
* 开漏输出 P-MOS输出是无效的,只有N-MOS在工作 这种模式下,只有低电平有驱动能力.高电平是没有驱动能力的
* 开漏模式可以作为通信协议的驱动方式,如I2C通信的引脚就是使用的开漏模式,多机通信下这个模式可以避免多个设备相互干扰
* 另外开漏模式还可以用于输出5V的电平信号,
* 关闭 当引脚配置为输入模式时,P-MOS和N-MOS都无效,输出关闭,端口的电平信号都由外部信号来控制
************************************************************************************************************************/

/************************************************************************************************************************
* | **模式名称** | **性质** | ************************特征***********************|
* | ------------ | -------- | -------------------------------------------------- |
* | 浮空输入     | 数字输入 | 可读取引脚电平,若引脚悬空,则电平不确定           |
* | 上拉输入     | 数字输入 | 可读取引脚电平,内部连接上拉电阻,悬空时默认高电平 |
* | 下拉输入     | 数字输入 | 可读取引脚电平,内部连接下拉电阻,悬空时默认低电平 |
* | 模拟输入     | 模拟输入 | GPIO无效,引脚直接接入内部ADC                      |
* | 开漏输出     | 数字输出 | 可输出引脚电平,高电平为高阻态,低电平接VSS        |
* | 推挽输出     | 数字输出 | 可输出引脚电平,高电平接VDD,低电平接VSS           |
* | 复用开漏输出 | 数字输出 | 由片上外设控制,高电平为高阻态,低电平接VSS        |
* | 复用推挽输出 | 数字输出 | 由片上外设控制,高电平接VDD,低电平接VSS           |
* 浮空输入 上拉输入 下拉输入 这三种模式的电路结构基本一致,区别是上下拉电阻的连接
* 开漏输出 推挽输出 这两种电路结构也基本一致 区别在于开漏输出的高电平呈现的是高阻态,没有驱动能力
* 复用开漏输出 复用推挽输出 这两种模式和普通的差不多,只不过复用的输出,引脚电平是由片上外设控制的
* 只有模拟输入会关闭数字的输入功能,其他模式数字输入都是有效的
************************************************************************************************************************/


/************************************************************************************************************************
* uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
* GPIO_ReadInputDataBit这个函数是用来读取输入寄存器某一个端口的输入值
* uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
* GPIO_ReadInputData这个函数是用来读取整个输入数据寄存器,返回值是uint16_t,是一个16位的数据,每一位代表一个端口值
* uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
* GPIO_ReadOutputDataBit这个函数是用来读取输出数据寄存器的某一个位,原则上来说它并不是用来读取端口的输入数据的,这个函数一般用于输出模式下,看一下自己输出的是什么
* uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
* GPIO_ReadOutputData这个函数是用来读取整个输出寄存器的
************************************************************************************************************************/

uint8_t Key_Num;

int main(void)
{
	/***********************************************************************************
		GPIO输入
	***********************************************************************************/
	LED_Init();
	Key_Init();
	
	while(1)
	{
		Key_Num = Key_GetNum();
		if(Key_Num == 1)
		{
			LED1_Turn();
		}
		if(Key_Num == 2)
		{
			LED2_Turn();
		}
		
	}

}

LED.c#
#include "stm32f10x.h"                  // Device header
#include "LED.h"

void LED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);//设置为高电平
}

void LED1_ON(void)
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}

void LED1_OFF(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_1);
}

void LED1_Turn(void)
{
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_1);
	}
	else
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_1);
	}
}

void LED2_ON(void)
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}

void LED2_OFF(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_2);
}

void LED2_Turn(void)
{
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2) == 0)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_2);
	}
	else
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_2);
	}
}


key.c#
#include "Key.h"

void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
	{
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
		Delay_ms(20);
		KeyNum = 1;
	}
	
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
	{
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
		Delay_ms(20);
		KeyNum = 2;
	}
	
	return KeyNum;
}

GPIO 使用方法总结

初始化时钟
定义结构体
赋值结构体
GPIO_Init 函数将指定的 GPIO 外设初始化好
读写 GPIO 的 8 个主要函数

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);
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。