三、GPIO

发布于:2025-04-04 ⋅ 阅读:(27) ⋅ 点赞:(0)

一、GPIO简介

  • GPIO(General Purpose Input Output)通用输入输出口
  • GPIO引脚电平:0V(低电平)~3.3V(高电平),部分引脚可容忍5V
    • 容忍5V,即部分引脚输入5V的电压,也认为是高电平;引脚定义中有FT(Five Tolerate)即表示可容忍5V;
    • 对于输出而言,最大就只能输出3.3V,因为供电就只有3.3V;
  • 可配置为8种输入输出模式;
  • 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等;
  • 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等;

二、GPIO基本结构

1.GPIO整体结构

在这里插入图片描述

  • STM32上,所有的GPIO都是挂载在APB2时钟总线上;
  • GPIO外设的名称按照GPIOA、B、C等等来命名的;
  • 每个GPIO外设有16个引脚,编号0~15;
  • 每个GPIO模块内,主要包含了寄存器和驱动器;
    • 寄存器就是一段特殊的存储器,内核可以通过APB2总线对寄存器进行读写,从而实现电平的输出和读取功能;
      • 寄存器的每一位对应一个引脚;
      • 输出寄存器写1,对应引脚就会输出高电平;写0,就会输出低电平;
      • 输入寄存器读取为1,对应引脚目前为高电平;读取为0,为低电平;
      • STM32为32位的单片机,所以STM32内部的寄存器都是32位,但每个GPIO只有16个引脚,所以寄存器只有低16位有对应的引脚,高16位是没有用到的;
    • 驱动器是用来增加信号的驱动能力,寄存器只负责存储数据;当进行点灯这样的操作,还是需要驱动器增大驱动能力;

2.GPIO位结构

在这里插入图片描述

  • 保护二极管:用于限制引脚的输入电压;
    • 上面保护二极管接VDD(3.3V),下面二极管接VSS(0V);
    • 当输入电压高于3.3V时,上方二极管导通,输入电压产生的电流就会直接流入VDD而不会流入内部电路,避免过高的电压对内部电路产生伤害;
    • 当输入电压低于0V时,下方二极管导通,电流会直接从IO引脚流出去,而不会从内部电路汲取电流,从而保护内部电路;
    • 当输入电压位于0~3.3V之间,上下方二极管均不会导通;
  • 上下拉电阻
    • 上下拉电阻的作用是给输入提供一个默认的输入电平,上下拉电阻均断开时,引脚处于浮空状态,此时引脚的输入电平极易受到外界干扰而改变;
    • 上拉电阻连接VDD(3.3V);下方电阻连接VSS(0V);连接开关可通过程序进行配置;
    • 上拉电阻打开,下拉电阻断开,即上拉输入模式,又称作默认为高电平的输入模式;
    • 上拉电阻断开,下拉电阻打开,即下拉输入模式,又称作默认为低电平的输入模式;
    • 上拉电阻和下拉电阻均断开,即浮空输入模式;
    • 上下拉电阻的阻值都是比较大的,是一种弱上拉和弱下拉,目的是尽量不影响正常的输入操作;
  • 施密特触发器:对输入电压进行整形
    • 当施密特触发器的输入电压大于某一阈值时,输出就会瞬间升为高电平;小于某一阈值,输出就会瞬间降为低电平;
    • 例如:引脚的波形为外界输入,虽然是数字信号,但实际情况下可能产生各种失真,一下面波形举例:
      在这里插入图片描述
  • 数据选择器:选择是输出数据寄存器还是片上外设(复用功能输出)的信号输入到输出控制;
    • 位设置/清除寄存器:用来单独操作输出数据寄存器的某一位,而不影响其他位(即单独操作GPIO中某一引脚的电平高低,输出寄存器同时控制GPIO的16个端口,且只能整体读写 )
  • MOS管:MOS管可以理解成电子开关,输出控制的信号控制开关的导通和关闭,进而控制IO口连接到VDD(3.3V)或VSS(0V);可选择推挽、开漏、关闭三种输出模式
    • 推挽输出模式:P-MOS和N-MOS均有效;这种模式下,高低电平均有较强的驱动能力,所以推挽输出模式也称为强推输出模式;在推挽输出模式下,STM32对IO口具有绝对的控制权,高低电平都有STM32控制;
      • 输出控制输出1时,P-MOS导通,N-MOS断开,输出直接接到VDD(3.3V),输出高电平;
      • 输出控制输出0时,P-MOS断开,N-MOS导通,输出直接接到VSS(0V),输出低电平;
    • 开漏输出模式:P-MOS无效,N-MOS有效;这种模式下,只有低电平有驱动能力,高电平没有驱动能力;开漏模式常用作通信协议的输出方式,比如I2C通信的引脚;在多机通信的情况下,开漏模式可以避免各个设备的相互干扰;
      • 输出控制输出1时,P-MOS断开,N-MOS断开,输出相当于断开,即高阻模式;
      • 输出控制输出0时,P-MOS断开,N-MOS导通,输出直接接到VSS(0V),输出低电平;
      • 开漏模式还可以用于输出5V的电平信号:在IO口外接一个上拉电阻到5V的电源,输出低电平时,由内部的N-MOS直接接VSS(0V);输出高电平时,由外部上拉电阻拉高至5V;这样就可以输出5V信号,用于兼容一些5V的电平设备;
    • 关闭模式:当引脚配置为输入模式时,P-MOS和N-MOS均无效,端口的电平由外部信号来控制;

三、GPIO的8种工作模式

在这里插入图片描述

  • 上拉/下拉/浮空输入配置
    在这里插入图片描述
  • 模拟输入配置
    在这里插入图片描述
  • 推挽/开漏输出配置
    在这里插入图片描述
  • 复用推挽/开漏输出配置
    在这里插入图片描述
  • 在输出模式下,输入都是有效的;在输入模式下,输出都是无效的;(因为一个端口只能有一个输出,但可以有多个输入);
  • 在GPIO的8种模式下,除了模拟输入模式会关闭数字输入功能,其他7种模式下,数字输入都是有效的;

四、参考手册_GPIO部分介绍

  • STM32F103数据手册和参考手册 蓝奏云下载链接,密码:2nkx
    在这里插入图片描述

  • 端口配置寄存器
    在这里插入图片描述

    • 每一个端口的模式由4位进行配置, 16个端口就需要64位,每个寄存器32位,所以配置寄存器有2个;
    • GPIO的输出速度可以限制输出引脚的最大翻转速度,该设计是为了低功耗和稳定性,一般要求不高时,配置成50MHz即可;
  • 端口输入寄存器
    在这里插入图片描述

    • 低16位对应16个引脚,高16位没有使用;
  • 端口输出寄存器
    在这里插入图片描述

    • 低16位对应16个引脚,高16位没有使用;
  • 端口位设置/清除寄存器
    在这里插入图片描述

    • 高16位用于位清除,低16位用于位设置; 写1用于设置或清除,写0不产生影响;
  • 端口位清除寄存器
    在这里插入图片描述

    • 低16位效果和端口位设置/清除寄存器的高16位功能一样;
    • 当只需要单一的进行位设置或位清除,位设置时,用端口位设置/清除寄存器;位清除时,用端口位清除寄存器;(此时进行位设置和位清除时,使用的都是低16位的数据,比较方便);
    • 当需要对多个端口同时进行位设置和位清除,可以使用端口设置/清除寄存器,这样可以保证位设置和位清除的同步性;

五、GPIO输出实验_外围设备介绍

1.LED和蜂鸣器

(1)LED和蜂鸣器简介

  • LED,发光二极管,正向通电点亮,反向通电不亮;
    在这里插入图片描述
  • 蜂鸣器:分为有源蜂鸣器和无源蜂鸣器
    • 有源蜂鸣器:内部自带震荡源,正负极接上直流电压即可持续发声,频率固定;
      在这里插入图片描述

    • 无源蜂鸣器:内部不带震荡源,需要控制器提供震荡脉冲才可发声;调整提供的震荡脉冲的频率,可发出不同频率的声音;

(2)LED和蜂鸣器硬件电路

在这里插入图片描述

  • STM32的GPIO在推挽输出模式下,高低电平具有比较强的驱动能力,选用高电平驱动或者低电平驱动均可;
  • 单片机电路种,一般倾向于低电平驱动,因为很多单片机或芯片,都是用了高电平弱驱动,低电平强驱动的规则(这样可以一定程度上避免高低电平冲突);

2.面包板

在这里插入图片描述
在这里插入图片描述

六、GPIO输出实验

1.LED闪烁

  • 接线图
    在这里插入图片描述

  • 操作STM32的GPIO外设一共需要3个步骤:

    • 使用RCC(Reset Clock Control,复位时钟控制),开启GPIO的时钟;
    • 使用GPIO_Init()函数初始化GPIO;
    • 使用输出或输入函数控制GPIO口;
  • RCC外设常用的3个库函数
    在这里插入图片描述
    在这里插入图片描述

  • GPIO常用的库函数:GPIO_Init、GPIO的8个读写函数
    在这里插入图片描述

    • GPIO的4个输出函数
      在这里插入图片描述
  • Delay延时函数

    • Delay.c
    #include "stm32f10x.h"
    
    /**
      * @brief  微秒级延时
      * @param  xus 延时时长,范围:0~233015
      * @retval 无
      */
    void Delay_us(uint32_t xus)
    {
    	SysTick->LOAD = 72 * xus;				//设置定时器重装值
    	SysTick->VAL = 0x00;					//清空当前计数值
    	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
    	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
    	SysTick->CTRL = 0x00000004;				//关闭定时器
    }
    
    /**
      * @brief  毫秒级延时
      * @param  xms 延时时长,范围:0~4294967295
      * @retval 无
      */
    void Delay_ms(uint32_t xms)
    {
    	while(xms--)
    	{
    		Delay_us(1000);
    	}
    }
     
    /**
      * @brief  秒级延时
      * @param  xs 延时时长,范围:0~4294967295
      * @retval 无
      */
    void Delay_s(uint32_t xs)
    {
    	while(xs--)
    	{
    		Delay_ms(1000);
    	}
    } 
    
    
    • Delay.h
    #ifndef __DELAY_H
    #define __DELAY_H
    
    void Delay_us(uint32_t us);
    void Delay_ms(uint32_t ms);
    void Delay_s(uint32_t s);
    
    #endif
    
    
  • main.c

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

int main(void)
{
	//1.打开GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		
	
	//2.配置PA0为推挽输出
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	

	while(1)
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_0);		// 打开LED
		Delay_ms(500);
		GPIO_SetBits(GPIOA, GPIO_Pin_0);		// 关闭LED
		Delay_ms(500);
	}

}


2.LED流水灯

  • 接线图
    在这里插入图片描述
  • main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

int main(void)
{
	//1.打开GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		
	
	//2.配置PA0-PA7为推挽输出
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	

	while(1)
	{
		GPIO_Write(GPIOA, ~0x0001);
		Delay_ms(500);
		GPIO_Write(GPIOA, ~0x0002);
		Delay_ms(500);
		GPIO_Write(GPIOA, ~0x0004);
		Delay_ms(500);
		GPIO_Write(GPIOA, ~0x0008);
		Delay_ms(500);
		GPIO_Write(GPIOA, ~0x0010);
		Delay_ms(500);
		GPIO_Write(GPIOA, ~0x0020);
		Delay_ms(500);
		GPIO_Write(GPIOA, ~0x0040);
		Delay_ms(500);
		GPIO_Write(GPIOA, ~0x0080);
		Delay_ms(500);
	}

}

3.蜂鸣器

  • 接线图
    在这里插入图片描述
  • main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

int main(void)
{
	//1.打开GPIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		
	
	//2.配置PA0-PA7为推挽输出
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	

	while(1)
	{
		GPIO_ResetBits(GPIOB, GPIO_Pin_12);
		Delay_ms(100);
		GPIO_SetBits(GPIOB, GPIO_Pin_12);
		Delay_ms(100);
		GPIO_ResetBits(GPIOB, GPIO_Pin_12);
		Delay_ms(100);
		GPIO_SetBits(GPIOB, GPIO_Pin_12);
		Delay_ms(700);
	}

}

4.库函数的使用方法

七、GPIO输入实验_外围设备介绍

1.按键

  • 常用的输入设备,按下导通,松开断开;
  • 按键抖动:按键内部使用的是机械式弹簧片来进行通断,在按下和松手的瞬间会伴有一连串的抖动;
    • 按键的抖动时间比较短,通常在5~10ms,人眼是分辨不出来的,但是对于高速运行的单片机而言,5 ~ 10ms还是很漫长的,所以需要对抖动进行过滤,即按键消抖;
    • 最简单的过滤办法就是加一段延时,把抖动时间耗过去;
      在这里插入图片描述
  • 按键的电路
    在这里插入图片描述

2.传感器模块

  • 课程中有4个传感器模块:光敏电阻传感器、热敏电阻传感器、对射式红外传感器、反射式红外传感器;
  • 这些传感器模块都是利用传感器元件(光敏电阻/热敏电阻/红外接收管)的电阻会随着外界的模拟量变化而变化(光线越强,光敏电阻阻值越小;温度越高,热敏电阻阻值越小;红外光线越强,红外接收管的阻值越小;)
    • 电阻的变化不容易采集到,通常将传感器元件与定值电阻进行串联分压,这样就可以得到模拟电压的输出。对于电路来说,检测电压就比较容易;
    • 还可以通过电压比较器,对输出的模拟电压进行二值化,这样就可以得到数字电压输出;
      在这里插入图片描述
  • 传感器的电路
    在这里插入图片描述

八、GPIO输入实验

1. 按键控制LED

  • 接线图
    在这里插入图片描述
  • 读取引脚和端口的库函数
    在这里插入图片描述
  • LED.c
#include "stm32f10x.h"                  // Device header

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_ReadOutputDataBit(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_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_2);
	}
	else
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_2);
	}
}

  • LED.h
#ifndef __LED_H
#define __LED_H

void LED_Init(void);

void LED1_ON(void);
void LED1_OFF(void);
void LED1_Turn(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED2_Turn(void);

#endif

  • Key.c
#include "stm32f10x.h"                  // Device header
#include "Delay.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 Ket_GetNum(void)
{
	uint8_t KeyNum = 0;
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)				// 按键1按下
	{	
		Delay_ms(20);																					// 消抖
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);	// 等待抬起
		Delay_ms(20);																					// 消抖
		KeyNum = 1;																						// 按键1按下后松开
	}
	
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)				// 按键2按下
	{	
		Delay_ms(20);																						// 消抖
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);	// 等待抬起
		Delay_ms(20);																						// 消抖
		KeyNum = 2;																							// 按键2按下后松开
	}
	
	return KeyNum;
}


  • Key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h"                  // Device header

void Key_Init(void);
uint8_t Ket_GetNum(void);	

#endif

2.光敏传感器控制蜂鸣器

  • 接线图
    在这里插入图片描述
  • Buzzer.c
#include "stm32f10x.h"                  // Device header


void Buzzer_Init(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;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

void Buzzer_ON(void)
{
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}

void Buzzer_OFF(void)
{
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

void Buzzer_Turn(void)
{
	if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0)
	{
		GPIO_SetBits(GPIOB, GPIO_Pin_12);
	}
	else
	{
		GPIO_ResetBits(GPIOB, GPIO_Pin_12);
	}
}

  • Buzzer.h
#ifndef __BUZZER_H
#define __BUZZER_H

void Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
void Buzzer_Turn(void);

#endif

  • LightSensor.c
#include "stm32f10x.h"                  // Device header

void LightSeneorInit(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;				// 光敏传感器,光照强时,输出低电平,输出指示灯亮;光照弱时,输出高电平,输出指示灯灭;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_13);
}

uint8_t LightSensor_Get(void)
{
	return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
}

  • LightSensor.h
#ifndef __LightSensor_H
#define __LightSensor_H
#include "stm32f10x.h"                  // Device header

void LightSeneorInit(void);
uint8_t LightSensor_Get(void);

#endif

  • main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"

int main(void)
{
	Buzzer_Init();
	LightSeneorInit();

	while(1)
	{
		if(LightSensor_Get() == 1)
		{
			Buzzer_ON();
		}
		else
		{
			Buzzer_OFF();
		}
	}

}



网站公告

今日签到

点亮在社区的每一天
去签到