当前位置: 首页 > news >正文

05-GPIO原理

一、概述

1、GPIO,即通用I/O(输入/输出)端口,是STM32可控制的引脚。STM32芯片的GPIO引脚与外部设备连接起来,可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。

 2、GPIO的复用:引脚复用是指将单个引脚配置为多个功能的能力。在 STM32 中,每个引脚都可以配置为多个不同的功能,如GPIO(输入/输出数据)、定时器、UART、SPI等。这样一来,通过配置引脚复用功能,可以实现多种硬件功能的连接和实现,提高了芯片的灵活性和可扩展性。引脚默认为IO口。

二、GPIO的工作模式

 1、八大模式和四种输出速度

4种输入模式

(1)浮空输入(即不连接内部上下拉电阻)

(2)上拉输入(连接上拉电阻)

(3)下拉输入(连接下拉电阻)

(4)模拟输入(用于检测模拟信号的输入)

4种输出模式 

(5)开漏输出(带上拉或者下拉)

(6)复用开漏输出(带上拉或者下拉)

(7)推挽输出(带上拉或者下拉):程序员可以输出高电平与低电平

(8)复用推挽输出(带上拉或者下拉):程序员可以低电平,想要输出电平,芯片外部需要有上拉电阻。

4种最大输出速度

(1)2MHZ  (低速)

(2)25MHZ  (中速)

(3)50MHZ  (快速)

(4)100MHZ  (高速)

2、上下拉电阻

上拉电阻:

  • 将一个不确定的信号,通过一个电阻与电源VCC相连,固定在高电平;
  • 上拉是对器件注入电流;灌电流
  • 当一个接有上拉电阻的IO端口设置为输入状态时,它的常态为高电平;

下拉电阻:

  • 将一个不确定的信号,通过一个电阻与地GND相连,固定在低电平;
  • 下拉是从器件输出电流;拉电流
  • 当一个接有下拉电阻的IO端口设置为输入状态时,它的常态为低电平;

 三、寄存器编程

1、基本概念

寄存器是微处理器或微控制器内部的小型存储单元,用于临时存储数据、指令或控制信号。寄存器通常由硬件实现,并且可以直接通过CPU访问。寄存器的设计目的是为了提高处理器的性能,因为它们比内存访问更快。在微控制器如STM32F407中,寄存器用于配置和控制各种外设的功能。

2、寄存器分类

  1. 通用寄存器:用于存储数据或作为临时存储空间。
  2. 状态寄存器:存储有关处理器状态的信息,如标志位。
  3. 控制寄存器:用于控制处理器的行为。
  4. 外设寄存器:用于配置和控制微控制器中的外设。

 3、常见寄存器及用途

GPIO寄存器:- GPIOx_MODER:配置GPIO引脚的工作模式(输入、输出、复用功能等)。
- GPIOx_OTYPER:配置GPIO引脚的输出类型(推挽或开漏)。
- GPIOx_OSPEEDR:配置GPIO引脚的输出速度。
- GPIOx_PUPDR:配置GPIO引脚的上拉/下拉电阻。
- GPIOx_IDR:读取GPIO引脚的输入值。
- GPIOx_ODR:设置GPIO引脚的输出值。
- GPIOx_BSRR:设置或清除GPIO引脚的输出值。
- GPIOx_LCKR:锁定GPIO引脚的配置。时钟控制寄存器:
- RCC_CR:控制时钟源的选择和启动。
- RCC_PLLCFGR:配置PLL(Phase-Locked Loop)时钟。
- RCC_CFGR:配置时钟树,包括AHB/APB总线时钟。
- RCC_CIR:时钟中断寄存器。中断寄存器:
- NVIC_ISER:中断使能寄存器。
- NVIC_ICER:中断清除使能寄存器。
- NVIC_ISPR:中断挂起寄存器。
- NVIC_ICPR:中断清除挂起寄存器。
- NVIC_IPR:中断优先级寄存器。

4、寄存器地址 = 外设的基地址+偏移地址

下面的边界地址即为外设的基地址

例如

RCC_AHB1ENR寄存器地址 = RCC基地址+偏移地址

RCC_AHB1ENR寄存器地址 = 0x40023800+0x30

寄存器分析;

5、寄存器点灯例子:

(1)理解LED电路原理图

LED0连接在PF9

PF9输出低电平,灯亮;输出高电平,灯灭

led.c文件代码:

#include "led.h"/************************************
引脚说明:
LED0连接在PF9
PF9输出低电平,灯亮;输出高电平,灯灭************************************/void Led_Init(void)
{    //将第5位置1,你使能GPIOF组时钟RCC_AHB1ENR |= (0x01<<5);//配置PF9输出模式GPIOF_MODER &= ~(0x01<<19); //19位清0GPIOF_MODER |= (0x01<<18);  //18位置1//配置PF9推挽输出GPIOF_OTYPER &= ~(0x01<<9); //9位清0//配置PF9中速 25MHZGPIOF_OSPEEDR &= ~(0x01<<19); //19位清0GPIOF_OSPEEDR |= (0x01<<18);  //18位置1    //配置PF9中速无上下拉GPIOF_PUPDR &= ~(0x01<<19); //19位清0GPIOF_PUPDR &= ~(0x01<<18); //18位清0
}

main.c文件:


#include <stdio.h>
#include "led.h"//粗延时(就是延时不一定准确的意思)void delay(int n)
{int i, j;for(i=0; i<n; i++)for(j=0; j<10000; j++);
}int main(void)
{//初始化后GPIOF_ODR默认为低电平,所以灯亮Led_Init();while(1){//灯亮GPIOF_ODR &= ~(0x01<<9);  //9位清0delay(1000);//灯灭GPIOF_ODR |= (0x01<<9);  //9位置1delay(1000);        }return 0;
}

led.h文件:

#ifndef __LED_H
#define __LED_H//注意:0x40023800这个地址是根据不同的寄存器来决定的,编写多个寄存器的时候要非常注意有没有改
#define  RCC_AHB1ENR   	*((volatile unsigned int *)(0x40023800 + 0x30)) //值强制转换为地址  通过解引用访问地址空间
#define  GPIOF_MODER   	*((volatile unsigned int *)(0x40021400 + 0x00)) 
#define  GPIOF_OTYPER   *((volatile unsigned int *)(0x40021400 + 0x04)) 
#define  GPIOF_OSPEEDR  *((volatile unsigned int *)(0x40021400 + 0x08)) 
#define  GPIOF_PUPDR    *((volatile unsigned int *)(0x40021400 + 0x0C)) 
#define  GPIOF_ODR    	*((volatile unsigned int *)(0x40021400 + 0x14))
void Led_Init(void);#endif

四、使用库函数编程来点灯的例子

1、背景

🔸优点:

  • 性能优化: 直接通过寄存器进行操作可以减少中间层的调用,提高程序执行效率。
  • 灵活性高: 寄存器编程允许开发者直接控制硬件,提供最大的灵活性来定制特定的功能。
  • 代码紧凑: 通常情况下,寄存器级别的编程会生成更紧凑的代码,有助于节省内存空间。

🔸缺点:

  • 易出错: 寄存器编程需要对硬件有深入的理解,容易因为配置错误而导致问题。
  • 移植性差: 不同的微控制器可能有不同的寄存器布局和功能,这使得代码难以在不同的硬件之间移植。
  • 调试困难: 错误往往不易被发现,调试过程可能会比较复杂且耗时。

2、库函数的使用步骤

1、需要在keil5中添加RCC和GPIO库

2、使能端口F(PF9属于GPIOF组)的硬件时钟(芯片所有外设都是关闭时钟,原因为了降低功耗)

//使能GPIOF组时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);

3、(3)初始化引脚功能(推挽输出 速度 上/下拉)

  GPIO_InitTypeDef    GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_9; //引脚9GPIO_InitStruct.GPIO_Mode    = GPIO_Mode_OUT;//输出GPIO_InitStruct.GPIO_OType    = GPIO_OType_PP;//推挽模式GPIO_InitStruct.GPIO_Speed    = GPIO_Speed_25MHz;//25MHZ速度GPIO_InitStruct.GPIO_PuPd    = GPIO_PuPd_NOPULL; //无上下拉GPIO_Init(GPIOF, &GPIO_InitStruct);

4、设置引脚电平

GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) //设置引脚为高电平
GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)//设置引脚为低电平

3、库函数开发例子2(按键)

(1)理解KEY电路原理图

KEY0连接PA0

KEY0按下,PA0为低电平

KEY0未按下,PA0为高电平

2、使能GPIO时钟

    //使能GPIOA组时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);    

3、配置PA0引脚

引脚输入时,不需要配置GPIO 端口输出类型及GPIO 端口输出速度

    //结构体变量GPIO_InitTypeDef    GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_0;     //引脚0GPIO_InitStruct.GPIO_Mode    = GPIO_Mode_IN;    //输入模式GPIO_InitStruct.GPIO_PuPd    = GPIO_PuPd_UP; //根据外面电路来设置即可

4、读引脚电平

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

具体代码:四个按键控制四个灯

key.h文件:

#ifndef   _KEY_H
#define   _KEY_H#include "stm32f4xx.h"void key_init(void);
void led_init(void);enum
{key0 = 1,key1,key2,key3,
};u8 Key_Scan(u8 mode);#endif

key.c文件:

#include "key.h"//key2,key3,key4的引脚为PE组的2,3,4void key_init(void)
{// 使能GPIOA组时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; // 入GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // 上拉GPIO_Init(GPIOA, &GPIO_InitStruct);// 使能GPIOE组时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);GPIO_InitTypeDef GPIO_InitStruct1;GPIO_InitStruct1.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_IN; // 入GPIO_InitStruct1.GPIO_PuPd = GPIO_PuPd_UP; // 上拉GPIO_Init(GPIOE, &GPIO_InitStruct1);
}void led_init(void)
{// 使能GPIOF组时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // 引脚9GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;			 // 输出GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;			 // 推挽模式GPIO_InitStruct.GPIO_Speed = GPIO_Speed_25MHz;		 // 25MHZ速度GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;		 // 无上下拉GPIO_Init(GPIOF, &GPIO_InitStruct);// 使能GPIOE组时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);GPIO_InitTypeDef GPIO_InitStruct1;GPIO_InitStruct1.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14; // 引脚13GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_OUT;			   // 输出GPIO_InitStruct1.GPIO_OType = GPIO_OType_PP;		   // 推挽模式GPIO_InitStruct1.GPIO_Speed = GPIO_Speed_25MHz;		   // 25MHZ速度GPIO_InitStruct1.GPIO_PuPd = GPIO_PuPd_NOPULL;		   // 无上下拉GPIO_Init(GPIOE, &GPIO_InitStruct1);// 关闭所有LED(假设低电平点亮LED)GPIO_SetBits(GPIOF, GPIO_Pin_9 | GPIO_Pin_10);GPIO_SetBits(GPIOE, GPIO_Pin_13 | GPIO_Pin_14);
}void delays(int n)
{int i, j;for (i = 0; i < n; i++)for (j = 0; j < 10000; j++);
}// u8 == unsigned char
/************************************
函数功能:按键扫描
返回值:
成功:按下返回按键标志位,如:KEY0标志位1,KEY1标志位2,KEY2标志位3,KEY3标志位4
失败:0u8 mode:是否支持连按
0:不支持连按
1:支持连按*************************************/
u8 Key_Scan(u8 mode)
{// key_up保存上一次的值static u8 key_up = 1; // 按键标志,表示未按下if (mode == 1) // 支持连按key_up = 1;if (key_up && (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) | (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_2) == 0) | (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3) == 0) | (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0)){delays(15);key_up = 0; // 表示按下(防止重复触发)if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0){return key0; // KEY0标志值为1}if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_2) == 0){return key1; // KEY0标志值为1}if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3) == 0){return key2; // KEY0标志值为1}if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0){return key3; // KEY0标志值为1}}else if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1 | GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_2) == 1 | GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3) == 1 | GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 1) // 未按下--也可以理解为松开{key_up = 1; // 按键松开key_up == 1}// 未操作按键(按键松开)return 0;
}

main.c文件:

#include <stdio.h>
#include "key.h"//粗延时(就是延时不一定准确的意思)void delay(int n)
{int i, j;for(i=0; i<n; i++)for(j=0; j<10000; j++);
}int main()
{//初始化后GPIOF_ODR默认为低电平,所以灯亮key_init();led_init();u8 value = 0;u8 key = 0;while(1){     //支持连按key = Key_Scan(1);if(key == key2){GPIO_ToggleBits(GPIOE, GPIO_Pin_13);}//不支持连按value = Key_Scan(0);if(value == key0){GPIO_ToggleBits(GPIOF, GPIO_Pin_9);}if(value == key1){GPIO_ToggleBits(GPIOF, GPIO_Pin_10);}
//		if(value == 3)
//		{
//			GPIO_ToggleBits(GPIOE, GPIO_Pin_13);
//		}if(value == key3){GPIO_ToggleBits(GPIOE, GPIO_Pin_14);}}return 0;
}

五、应用领域:如常见家里的电子门锁。

相关文章:

  • Bolsig+超详细使用教程
  • toCharArray作用
  • Java知识日常巩固(五)
  • 【torch\huggingface默认下载路径修改】.cache/torch/ 或 .cache/huggingface
  • 【C到Java的深度跃迁:从指针到对象,从过程到生态】第四模块·Java特性专精 —— 第十三章 异常处理:超越C错误码的文明时代
  • 【2025 最新前沿 MCP 教程 01】模型上下文协议:AI 领域的 USB-C
  • 支付宝小程序组件与页面构造器使用指南:从页面到组件的正确迁移
  • 第25周:DenseNet+SE-Net实战
  • 抖音集团电商流量实时数仓建设实践
  • 制作一个简单的操作系统10
  • 第R4周:LSTM-火灾温度预测
  • RocketMQ 主题与队列的协同作用解析(既然队列存储在不同的集群中,那要主题有什么用呢?)---管理命令、配置安装(主题、消息、队列与 Broker 的关系解析)
  • java多线程(6.0)
  • 探秘 3D 展厅之卓越优势,解锁沉浸式体验新境界
  • DeepSeek本地部署保姆级教程
  • shell脚本3
  • 【基础IO上】复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux系统下,一切皆文件 | 重定向原理
  • OpenAI 最新 o3 集成到 Cursor 和 Cline 工作流程中
  • SIEMENS PLC 程序 GRAPH 程序解读 车型入库
  • 从桥梁坍塌到地质隐患:超导磁测量技术的“防患未然”价值
  • ​王毅会见塔吉克斯坦外长穆赫里丁
  • 对外投资增长、消费市场持续升温,中国经济砥砺前行
  • 特朗普说克里米亚将留在俄罗斯,泽连斯基:绝不承认
  • 巴印在克什米尔发生交火
  • 农贸美学、业态再构、智能管理,今天的菜市场不止有菜
  • 韩国对华中厚板征收临时反倾销税