STM32入门笔记01_GPIO通用输入输出口介绍及GPIO输入输出案例:基于江科大STM32教程

说明:

单片机小白,迄今为止第三次学习单片机(前两次点完灯,因为一些其他的事情就没有继续学习了),是个点灯带师。这学期刚好开了单片机的课程,因此希望通过写笔记的方式督促自己的学习。目前除了听自己老师上课外,还在B站跟着江科大的教程学习,此笔记是基于江科大STM32教程,大部分原理图片均为江科大ppt里复制来的。但是,本笔记案例相较于江科大的有些许改进, 为锻炼自己的代码能力,对多个单独的模块进行统一的控制,对于改进的代码也有相应的实物接线图和说明。

江科大STM32教程链接:【STM32入门教程-2023持续更新中】 https://www.bilibili.com/video/BV1th411z7sn/?share_source=copy_web&vd_source=ee06a25b3dfb2900ab707b01fdff6667

GPIO通用输入输出口(挂载在APB2外设)

GPIO简介

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

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

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

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

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

GPIO位结构

GPIO模式

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

模式名称

性质

特征

浮空输入

数字输入

可读取引脚电平,若引脚悬空,则电平不确定

上拉输入

数字输入

可读取引脚电平,内部连接上拉电阻,悬空时默认高电平

下拉输入

数字输入

可读取引脚电平,内部连接下拉电阻,悬空时默认低电平

模拟输入

模拟输入

GPIO无效,引脚直接接入内部ADC

开漏输入

数字输入

可输出引脚电平,高电平为高阻态,低电平接VSS

(高电平没有驱动能力)

推挽输入

数字输入

可输出引脚电平,高电平接VDD,低电平接VSS

复用开漏输入

数字输入

由片上外设控制,高电平为高阻态,低电平接VSS

(高电平没有驱动能力)

复用推挽输入

数字输入

由片上外设控制,高电平接VDD,低电平接VSS

浮空、上拉、下拉输入

模拟输入

开漏、推挽输出

复用开漏、推挽输出

注意:图中肖特基触发器为翻译错误,应为施密特触发器

GPIO输出案例: LED流水灯、Beep蜂鸣器

步骤:

1.RCC使能外设(APB2)

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/**
  * @brief  Enables or disables the High Speed APB (APB2) peripheral clock.
  * @param  RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11     
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)函数, 用来使能或失能APB2外设时钟。

第一个参数填需要使能或者失能的外设,第二个参数填ENABLE or DISABLE

2.配置GPIO初始化参数

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
unsigned int gpio_groups=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5
							|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Pin=gpio_groups;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

通过GPIO_InitTypeDef结构体配置GPIO的初始化参数

结构体内共有三个参数: GPIO_Pin GPIO_Speed GPIO_Mode

GPIO_Pin:
#define GPIO_Pin_0                 ((uint16_t)0x0001)  /*!< Pin 0 selected */
#define GPIO_Pin_1                 ((uint16_t)0x0002)  /*!< Pin 1 selected */
#define GPIO_Pin_2                 ((uint16_t)0x0004)  /*!< Pin 2 selected */
#define GPIO_Pin_3                 ((uint16_t)0x0008)  /*!< Pin 3 selected */
#define GPIO_Pin_4                 ((uint16_t)0x0010)  /*!< Pin 4 selected */
#define GPIO_Pin_5                 ((uint16_t)0x0020)  /*!< Pin 5 selected */
#define GPIO_Pin_6                 ((uint16_t)0x0040)  /*!< Pin 6 selected */
#define GPIO_Pin_7                 ((uint16_t)0x0080)  /*!< Pin 7 selected */
#define GPIO_Pin_8                 ((uint16_t)0x0100)  /*!< Pin 8 selected */
#define GPIO_Pin_9                 ((uint16_t)0x0200)  /*!< Pin 9 selected */
#define GPIO_Pin_10                ((uint16_t)0x0400)  /*!< Pin 10 selected */
#define GPIO_Pin_11                ((uint16_t)0x0800)  /*!< Pin 11 selected */
#define GPIO_Pin_12                ((uint16_t)0x1000)  /*!< Pin 12 selected */
#define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */
#define GPIO_Pin_14                ((uint16_t)0x4000)  /*!< Pin 14 selected */
#define GPIO_Pin_15                ((uint16_t)0x8000)  /*!< Pin 15 selected */
#define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*!< All pins selected */
GPIO_Speed:
typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
GPIO_Mode:
typedef enum
{ GPIO_Mode_AIN = 0x0,          // 模拟输入
  GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
  GPIO_Mode_IPD = 0x28,         // 下拉输入
  GPIO_Mode_IPU = 0x48,         // 上拉输入
  GPIO_Mode_Out_OD = 0x14,      // 开漏输出
  GPIO_Mode_Out_PP = 0x10,      // 推挽输出
  GPIO_Mode_AF_OD = 0x1C,       // 复用开漏输出
  GPIO_Mode_AF_PP = 0x18        // 复用推挽输出
}GPIOMode_TypeDef;

3.执行GPIO初始化函数

GPIO_Init(GPIOA, &GPIO_InitStructure);

第一个参数: 需要初始化GPIO端口, GPIO初始化结构体

4.while循环中控制GPIO输出信号

// 引脚高电平 
	GPIO_SetBits(GPIOA, gpio_groups);
	while(1)
	{
		if(idx==7){
			flag=0;
		}
		
		if(idx==0){
			flag=1;
		}
		//引脚低电平
		GPIO_ResetBits(GPIOA, leds[idx]);
		// 蜂鸣器
		GPIO_ResetBits(GPIOA, GPIO_Pin_8);
		// 延时
		Delay_ms(500);
		// 引脚高电平
		GPIO_SetBits(GPIOA, leds[idx]);
		// 蜂鸣器
		GPIO_SetBits(GPIOA, GPIO_Pin_8);
		// 延时
		Delay_ms(500);
		
		if(flag==1){
			idx++;
		}
		if(flag==0){
			idx--;
		}
	}

5.整体代码

#include "stm32f10x.h" 
#include "delay.h"
// LED 闪烁实验 蜂鸣器实验
// 2023年3月8日20:01:30
int main(void)
{
	unsigned leds[8] = {GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_2, GPIO_Pin_3, GPIO_Pin_4,
						GPIO_Pin_5, GPIO_Pin_6, GPIO_Pin_7};
	unsigned flag = 1;
	unsigned idx = 0;
	// RCC 使能APB2外设 GPIOA
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	// 配置GPIO初始化参数
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	unsigned int gpio_groups=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5
							|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Pin=gpio_groups;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	// 执行GPIO初始化函数
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	// 引脚高电平 
	GPIO_SetBits(GPIOA, gpio_groups);
	while(1)
	{
		if(idx==7){
			flag=0;
		}
		
		if(idx==0){
			flag=1;
		}
		//引脚低电平
		GPIO_ResetBits(GPIOA, leds[idx]);
		// 蜂鸣器
		GPIO_ResetBits(GPIOA, GPIO_Pin_8);
		// 延时
		Delay_ms(500);
		// 引脚高电平
		GPIO_SetBits(GPIOA, leds[idx]);
		// 蜂鸣器
		GPIO_SetBits(GPIOA, GPIO_Pin_8);
		// 延时
		Delay_ms(500);
		
		if(flag==1){
			idx++;
		}
		if(flag==0){
			idx--;
		}
	}
}

GPIO输入案例:按键控制流水灯、光敏传感器控制蜂鸣器

硬件介绍

按键:常见的输入设备,按下导通,松手断开

按键抖动:由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴随有一连串的抖动

传感器模块: 传感器元件(光敏电阻/热敏电阻/红外接收管等)的电阻会随外界模拟量的变化而变化,通过与定值电阻分压即可得到模拟电压输出,再通过电压比较器进行二值化即可得到数字电压输出

可以看出当光线较暗时,光敏电阻阻值增大,AO输出高电平。

实物接线图:

说明:

A0-A7接LED,蜂鸣器I/O口接A9; 两个按键分别接到A10、B10,一个高电平触发(A10),一个低电平触发(B10); 光敏传感器模块数字输出口DO接A8; VCC和GND的接线不做说明。

.C代码:

main.c:
#include "stm32f10x.h" 
#include "delay.h"
#include "LED.h"
#include "key.h"
#include "lightSensor.h"
#include "beep.h"
// 按键控制LED流水灯和光敏传感器控制蜂鸣器
// 2023年3月10日16:10:49
int main(void)
{
	Init_LED();  // 初始化LED 输出IO口
	Init_Key();  // 初始化按键 输入IO口
	Init_LightSensor();  // 初始化光敏传感器 输出IO口
	Init_Beep();  // 初始化蜂鸣器 输入IO口
	uint8_t mode = 0;
	while(1)
	{
		uint8_t sensorSignal = Get_SignalFromSensor();  // 光敏传感器获取信号
		uint8_t keyNum = Get_KeyNum(); // 按键序号
		//按键控制流水灯
		/*
		if(keyNum==1){
			mode = 1;
		}else if(keyNum==2){
			mode = 0;
		}
		*/
		mode = keyNum==1 ? 1 : keyNum==2 ? 0 : mode;
		mode = keyNum ? keyNum%2 : mode;
		/*
		if(mode==1){
			FlowLed();
		}else if(mode==0){
			Close_All_Lights();
		}
		*/
		mode==1 ? FlowLed(): Close_All_Lights();
		// 光敏传感器控制蜂鸣器
		/*
		if(sensorSignal==1){
			Beep_On();
		}else if(sensorSignal==0){
			Beep_Off();
		}
		*/
		sensorSignal==1 ? Beep_On() : Beep_Off();
		
	}
}
key.c
#include "stm32f10x.h"
#include "delay.h"

// 2023年3月10日17:00:13
// B10设置为上拉输入 默认高电平 按键按下后为低电平
// A10设置为下拉输入 默认低电平 按键按下后为高电平
void Init_Key(void)
{
	// GPIO初始化结构体配置参数
	GPIO_InitTypeDef GPIO_InitStructureA;
	GPIO_InitTypeDef GPIO_InitStructureB;
	// RCC时钟使能APB2外设
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	// 配置参数
	GPIO_InitStructureA.GPIO_Mode=GPIO_Mode_IPD;  // 下拉输入
	GPIO_InitStructureA.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructureA.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructureB.GPIO_Mode=GPIO_Mode_IPU;  // 上拉输入
	GPIO_InitStructureB.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructureB.GPIO_Pin=GPIO_Pin_10;
	// 调用初始化函数
	GPIO_Init(GPIOA, &GPIO_InitStructureA);
	GPIO_Init(GPIOB, &GPIO_InitStructureB);
	return;
}

// 判断哪个按键按下
uint8_t Get_KeyNum(void)
{
	uint8_t KeyNum=0;
	// A10按下 下拉输入 默认低电平 按键按下后为高电平
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)==1){
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)==1);
		Delay_ms(20);
		KeyNum=1;
	}
	if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)==0){
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)==1);
		Delay_ms(20);
		KeyNum=2;
	}
	return KeyNum;
}
led.c
#include "stm32f10x.h"
#include "delay.h"

// 初始化LED所用引脚 A0-A7
void Init_LED(void)
{
	// 八个引脚 A0-A7
	uint16_t GPIO_GROUP=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5
						|GPIO_Pin_6|GPIO_Pin_7;
	// RCC 使能APB2外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	// GPIO初始化结构体配置参数
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 推挽输出模式
	GPIO_InitStructure.GPIO_Pin=GPIO_GROUP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	// 调用GPIO初始化函数
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	// 设置高电平
	GPIO_SetBits(GPIOA, GPIO_GROUP);
	
	return;
}

// 流水灯
uint8_t FlowLed(void)
{
	static uint8_t flag=1;
	static uint8_t pin=0;
	uint16_t gpio_group[8] = {GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_2, GPIO_Pin_3, GPIO_Pin_4,
						GPIO_Pin_5, GPIO_Pin_6, GPIO_Pin_7};
	if((pin)==7){
		flag=0;
	}
		
	if((pin)==0){
		flag=1;
	}
	// 引脚低电平
	GPIO_ResetBits(GPIOA, gpio_group[pin]);
	// 延时
	Delay_ms(500);
	// 引脚高电平
	GPIO_SetBits(GPIOA, gpio_group[pin]);
	// 延时
	Delay_ms(500);
	
	if(flag==1){
		pin = pin+1;
	}
	if(flag==0){
		pin = pin-1;
	}	
	return 1;
}

// 关闭所有LED
uint8_t Close_All_Lights(void)
{
	uint16_t GPIO_GROUP=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5
						|GPIO_Pin_6|GPIO_Pin_7;
	GPIO_SetBits(GPIOA, GPIO_GROUP);
	return 1;
}
lightSensor.c
#include "stm32f10x.h"

// 2023年3月10日21:24:36
// 初始化A8引脚作为光敏传感器信号的输入
// 当光线变暗时光敏电阻阻值增大,分压上升,输出高电平
void Init_LightSensor(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	// RCC 使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	// 配置GPIO初始化结构体变量
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;  // 下拉模式 默认输入为低电平
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	// 调用GPIO初始化函数
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	return;
}

// 获取并光敏传感器输入到单片机里的信号 
uint8_t Get_SignalFromSensor(void)
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8);
}
beep.c
#include "stm32f10x.h"
#include "delay.h"
// 2023年3月10日21:26:17
// 通过A9引脚输出的信号控制蜂鸣器

// 初始化所需要的引脚
void Init_Beep(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	// RCC时钟使能APB2外设
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	// 配置初始化结构体的参数
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	// 调用GPIO初始化函数
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	// 设置高电平
	GPIO_SetBits(GPIOA, GPIO_Pin_9);
	return;
}

// 蜂鸣器鸣叫
uint8_t Beep_On(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_9);
	Delay_ms(200);
	GPIO_ResetBits(GPIOA, GPIO_Pin_9);
	Delay_ms(200);
	return 1;
}

uint8_t Beep_Off(void)
{
	// 高电平停止
	GPIO_SetBits(GPIOA, GPIO_Pin_9);
	return 1;
}

小结:

GPIO常用函数:

函数名称

功能

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

根据GPIO结构体函数中的参数(GPIO_Mode, GPIO_Pin, GPIO_Speed)初始化外设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);

向指定的GPIO数据端口写入数据

void GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

读取指定端口引脚的输入

void GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

读取指定的GPIO端口输入

void GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

读取指定端口引脚的输出

void GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

读取指定的GPIO端口输出

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值