STM32—GPIO外设

2. 外设—GPIO

本文来自于《STM32——江科大》的笔记整理。

2.1 GPIO简介

GPIO(General Purpose Input Output)通用输入输出口
可配置为8种输入输出模式
引脚电平:0V~3.3V,部分引脚可容忍5V(在STM32F103C8T6引脚定义中,带FT的,可以容忍5V)
输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等

2.2 GPIO基本结构

在STM32中,所有的GPIO都是挂在在APB2外设总线上的,其中GPIO外设的名称是按照GPIOA、GPIOB、GPIOC等等这样命名的,每一个GPIO外设,总共有16个引脚(0-15)命名:PA0,PA10,PB5,PB12

在这里插入图片描述

寄存器只负责存储数据,如果要进行点灯操作,还需要驱动器增强驱动能力

2.3 GPIO位结构

这里应该是施密特触发器,肖特触发器应该是一个翻译错误

**施密特触发器的作用:**对输入电压进行整形的,它的执行逻辑是,如果输入电影大于某一阈值,输出就会瞬间升为高电平,如果输入电压小于某一阈值,输出就会瞬间降为低电平
在这里插入图片描述

2.3.1 GPIO模式

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

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

2.3.2 浮空/上拉/下拉输入

在这里插入图片描述

2.3.3 模拟输入

在这里插入图片描述

2.3.4 开漏/推挽输出

在这里插入图片描述

2.3.5 复用开漏/推挽输出

在这里插入图片描述

位设置/清除寄存器
在这里插入图片描述

端口位清除寄存器

2.4 GPIO的输出

LED闪烁&LED流水灯&蜂鸣器

硬件电路

在这里插入图片描述

2.4.1 LED闪烁

拿出一个LED,长脚正极接到正极,短脚负极接到PA0端口上(低电平点亮)

新建LED点亮工程(可以赋值上面的工程模板,进行改动)

RCC常用库函数

在这里插入图片描述

GPIO常用库函数

在这里插入图片描述

我们可以在工程目录新建一个文件夹,叫做System,存放系统的资源。
在这里插入图片描述

代码:

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

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);
	}
} 

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
int main()
{
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置使能时钟
	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);
    
	//GPIO_SetBits(GPIOA,GPIO_Pin_0);//设置高电平 ,灭
    //GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//设置高电平 ,灭
    
	//GPIO_ResetBits(GPIOA,GPIO_Pin_0);//设置低电平,点亮
    //GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//设置低电平,点亮
	while(1)
	{
		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//设置低电平,点亮
        Delay_ms(500);
        GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//设置高电平 ,灭
        Delay_ms(500);
	}
}

2.4.2 LED流水灯

如果要控制多个端口可以进行按位或来进行操作

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 等等

这里我们也可以使用

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All 表示全部选择

代码:

可以同时控制16个端口

GPIO_Write(GPIOx,指定写到输出数据寄存器的值);//GPIOx->ODR = 指定写到输出数据寄存器的值

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
int main()
{
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;//选择A的所有端口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	while(1)
	{
		//低电平点亮,第一个亮,其他灯灭
		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);
	}
}

2.4.3 蜂鸣器

使用的PB12口

代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
int main()
{
//	RCC->APB2ENR=0x00000010;
//	GPIOC->CRH =0x00300000;
//	GPIOC->ODR =0x00002000;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,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);

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

2.5 GPIO的输入

按键控制LED&光敏传感器控制蜂鸣器

传感器模块介绍

在这里插入图片描述

硬件电路

在这里插入图片描述

介绍c语言的相关知识点
C语言的数据类型
关键字位数表示范围stdint关键字ST关键字
char8-128 ~ 127int8_ts8
unsigned char80 ~ 255uint8_tu8
short16-32768 ~ 32767int16_ts16
unsigned short160 ~ 65535uint16_tu16
int32-2147483648 ~ 2147483647int32_ts32
unsigned int320 ~ 4294967295uint32_tu32
long32-2147483648 ~ 2147483647
unsigned long320 ~ 4294967295
long long64-(2^64)/2 ~ (2^64)/2-1int64_t
unsigned long long640 ~ (2^64)-1uint64_t
float32-3.4e38 ~ 3.4e38
double64-1.7e308 ~ 1.7e308
C语言宏定义
关键字:#define
用途:用一个字符串代替一个数字,便于理解,防止出错;提取程序中经常出现的参数,便于快速修改
定义宏定义:
	#define ABC 12345
引用宏定义:
	int a = ABC;	//等效于int a = 12345;
C语言typedef
关键字:typedef
用途:将一个比较长的变量类型名换个名字,便于使用
定义typedef:
	typedef unsigned char uint8_t;
引用typedef:
	 uint8_t a;	//等效于unsigned char a;
C语言结构体
关键字:struct
用途:数据打包,不同类型变量的集合
定义结构体变量:
	struct{char x; int y; float z;} StructName;
	因为结构体变量类型较长,所以通常用typedef更改变量类型名
引用结构体成员:
	StructName.x = 'A';
	StructName.y = 66;
	StructName.z = 1.23;
或	pStructName->x = 'A';	//pStructName为结构体的地址	pStructName->y = 66;
	pStructName->z = 1.23;
C语言枚举
关键字:enum
用途:定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合
定义枚举变量:
	enum{FALSE = 0, TRUE = 1} EnumName;
	因为枚举变量类型较长,所以通常用typedef更改变量类型名
引用枚举成员:
	EnumName = FALSE;
	EnumName = TRUE;

2.5.1 按键控制LED

首先,模块化编程,封装驱动代码,简化主函数

我们在工程目录新建一个文件夹,Hardware,用来存放硬件驱动,在再keil新建一个组也叫Hardware,并添加到头文件路径列表中

在这里插入图片描述

封装GPIO功能(这里我们使用的 PA1 , PA2)

LED.c(LED闪烁)

#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(GPIOB, &GPIO_InitStructure);
	//这里注意,GPIO配置好了之后,默认是低电平
	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 LED2_ON(void)
{
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);//设置低电平
}
void LED2_OFF(void)
{
	GPIO_SetBits(GPIOA,GPIO_Pin_1);//设置高电平
}

LED.h(LED闪烁)

#ifndef __LED_H
#define __LED_H

void LED_Init(void);//声明该函数可以被外部调用
void LED1_ON(void);
void LED1_OFF(void);
void LED2_ON(void);
void LED2_OFF(void);
#endif

mian.c(LED闪烁)

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
#include "LED.h"   //加上LED.h,就可以调用函数了
int main()
{
  LED_Init();//可能会爆红,编译一下就好了

	while(1)
	{
		
		void LED1_ON(void);
		Delay_ms(500);
		void LED2_OFF(void);
		Delay_ms(500);
    void LED1_OFF(void);
		Delay_ms(500);
    void LED2_ON(void);
		Delay_ms(500);
    
	}
}

封装按键功能(这里我们使用的是PB1,PB11

按键的一端 接地

在这里插入图片描述

代码:

Key.c

#include "stm32f10x.h" 
#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 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;

}

Key.h

#ifndef __KEY_H
#define __KEY_H

void Key_Init(void);

uint8_t Key_GetNum(void);

#endif

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配置好了之后,默认是低电平
	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 LED2_ON(void)
{
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);//设置低电平
}
void LED2_OFF(void)
{
	GPIO_SetBits(GPIOA,GPIO_Pin_1);//设置高电平
}
//PA1端口的电平翻转
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);//设置低电平
	}
}
//PA2端口的电平翻转
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);//设置低电平
	}
}


LED.h(完整)

#ifndef __LED_H
#define __LED_H

void LED_Init(void);//声明该函数可以被外部调用
void LED1_ON(void);
void LED1_OFF(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED1_Turn(void);
void LED2_Turn(void);
#endif

main.c(完整)

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
#include "LED.h"   //加上LED.h,就可以调用函数了
#include "Key.h"

uint8_t KeyNum=0;//接收返回值
int main()
{
  LED_Init();//可能会爆红,编译一下就好了
  Key_Init();
	while(1)
	{
		KeyNum=Key_GetNum();//不断读取按键返回值
		if(KeyNum==1)
		{
			LED1_Turn();
		}else if(KeyNum==2)
		{
			LED2_Turn();
		}
	}
}

2.5.2 光敏传感器控制蜂鸣器

接线图

在这里插入图片描述

我们还是先封装对应功能

依旧是在Hardware文件夹内添加

封装蜂鸣器功能(PB12)

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配置好了之后,默认是低电平
	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);//设置高电平
}

//PB12端口的电平翻转
void Buzzer_Turn(void)
{
	if(GPIO_ReadInputDataBit(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);
//PB12端口的电平翻转
void Buzzer_Turn(void);
#endif

main.c

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

uint8_t KeyNum=0;//接收返回值
int main()
{
  Buzzer_Init();//可能会爆红,编译一下就好了
  
	while(1)
	{
    Buzzer_ON();
		Delay_ms(500);
		Buzzer_OFF();
		Delay_ms(500);
		
		Buzzer_Turn();
		Delay_ms(500);
		Buzzer_Turn();
		Delay_ms(500);
	}
}

封装光敏电阻模块(DO口连接PB13)

LightSensor.c

#include "stm32f10x.h"                  // Device header


void LightSensor_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_13;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
}
//读取端口值的函数
uint8_t LightSensor_Get(void)
{
	return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13);
}

LightSensor.h

#ifndef __LIGHTSENSOR_H
#define __LIGHTSENSOR_H

void LightSensor_Init(void);
//读取端口值的函数
uint8_t LightSensor_Get(void);

#endif

main.c

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


uint8_t KeyNum=0;//接收返回值
int main()
{
  Buzzer_Init();//可能会爆红,编译一下就好了
	LightSensor_Init();//可能会爆红,编译一下就好了
  
	while(1)
	{
    if(LightSensor_Get()==1)
		{
			Buzzer_ON();
		}
		else
		{
			Buzzer_OFF();
		}
	}
}

在这里插入图片描述

既然都看到最后了,可否给个呢,在这里插入图片描述关注收藏不迷路


给那些看完的朋友,奖励一个 赤赤博客-后端+前端,觉得不错的话可以推荐给身边的朋友哟!
在这里插入图片描述


  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安赫'

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值