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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/77264.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于LangChain4J的AI Services实践:用声明式接口重构LLM应用开发

基于LangChain4J的AI Services实践&#xff1a;用声明式接口重构LLM应用开发 前言&#xff1a;当Java开发遇上LLM编程困境 在LLM应用开发领域&#xff0c;Java开发者常面临两大痛点&#xff1a;一是需要手动编排Prompt工程、记忆管理和结果解析等底层组件&#xff0c;二是复杂…

深入解析 Docker 容器进程的 cgroup 和命名空间信息

深入解析 Docker 容器进程的 cgroup 和命名空间信息 在现代 Linux 系统中&#xff0c;控制组&#xff08;cgroup&#xff09;和命名空间&#xff08;namespace&#xff09;是实现容器化技术的核心机制。cgroup 用于管理和限制进程的资源使用&#xff08;如 CPU、内存、I/O&…

【汽车ECU电控数据管理篇】S19文件格式解析篇章

一、S19格式是啥 在电控文件管理的初期阶段&#xff0c;我首次接触到的是 A2L 和 HEX 文件。其中&#xff0c;A2L 文件主要承担着描述性功能&#xff0c;它详细地描述了各种参数和配置等相关信息。而 HEX 文件则是一种刷写文件&#xff0c;其内部明确记录了具体的地址以及对应的…

python编程相关的单词

the: 在编程中&#xff0c;“the” 是一个常见的英语单词&#xff0c;用于指定特定的对象或变量。例如&#xff0c;“the function” 指的是某个特定的函数。 the的拼写是t,h,e.再读一次t,h,e and: 在编程中&#xff0c;“and” 是一个逻辑运算符&#xff0c;用于连接两个条件&…

网络原理 - 4(TCP - 1)

目录 TCP 协议 TCP 协议段格式 可靠传输 几个 TCP 协议中的机制 1. 确认应答 2. 超时重传 完&#xff01; TCP 协议 TCP 全称为 “传输控制协议”&#xff08;Transmission Control Protocol&#xff09;&#xff0c;要对数据的传输进行一个详细的控制。 TCP 协议段格…

python博客爬虫列表

我希望对指定网页的&#xff0c;博客列表&#xff0c;获取url&#xff0c;然后保存成本地文件&#xff0c;用python实现 step1: import requests from bs4 import BeautifulSoup import jsondef get_blog_links(url):headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win6…

软件测试入门学习笔记

今天学习新知识&#xff0c;软件测试。 什么是软件测试&#xff1f; 使用人工和自动手段来运行或测试某个系统的过程&#xff0c;目的在于检验它是否满足规定的需求或弄清实际结果与预期结果之间的差别。 软件测试的目的&#xff1f; 1&#xff09;为了发现程序&#xff0…

uniapp开发2--uniapp中的条件编译总结

以下是对 uni-app 中条件编译的总结&#xff1a; 概念&#xff1a; 条件编译是一种技术&#xff0c;允许你根据不同的平台或环境&#xff0c;编译不同的代码。 在 uni-app 中&#xff0c;这意味着你可以编写一套代码&#xff0c;然后根据要编译到的平台&#xff08;例如微信小…

【k8s】sidecar边车容器

一、Sidecar 模式简介 Sidecar 模式是一种常见的微服务架构设计模式。它通过将附加功能或服务与主应用程序部署在同一容器或主机上&#xff0c;从而实现对主应用程序的增强和扩展。Sidecar 的名称来源于摩托车的边车&#xff0c;它与摩托车紧密相连&#xff0c;为主车提供额外…

MySQL索引使用一定有效吗?如何排查索引效果?

MySQL索引使用一定有效吗&#xff1f;如何排查索引效果&#xff1f; 1. 索引一定有效吗&#xff1f; 不一定&#xff01; 即使你创建了索引&#xff0c;MySQL 也可能因为以下原因 不使用索引 或 索引效果不佳&#xff1a; 索引选择错误&#xff1a;MySQL 优化器可能选择了错…

漏洞管理体系:从扫描评估到修复验证的全生命周期实践

漏洞管理体系&#xff1a;从扫描评估到修复验证的全生命周期实践 在网络安全防御体系中&#xff0c;漏洞管理是“攻防博弈”的核心战场。据NVD&#xff08;国家漏洞数据库&#xff09;统计&#xff0c;2023年新增漏洞超21万个&#xff0c;平均每天披露575个&#xff0c;其中32…

cdh平台管理与运维最佳实践

一、容量规划:构建可持续扩展的数据湖底座 1.1 资源评估三维模型 #mermaid-svg-4Fd5JDKTgwqF1BUd {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-4Fd5JDKTgwqF1BUd .error-icon{fill:#552222;}#mermaid-svg-4Fd5J…

力扣347:前K个高频元素

给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2]示例 2: 输入: nums [1], k 1 输出: [1]题解&#xff1a; 一、思路&#xff1a; 1.我希望将nu…

前馈神经网络层

FeedForward Network 论文地址 https://arxiv.org/pdf/1706.03762 前馈网络介绍 前馈网络是Transformer模型中的关键组件&#xff0c;每个Transformer层包含一个多头注意力模块和一个前馈网络模块。该模块通过两次线性变换和激活函数&#xff0c;为模型提供非线性建模能力。其核…

如何将 sNp 文件导入并绘制到 AEDT (HFSS)

导入 sNp 文件 打开您的项目&#xff0c;右键单击 “Result” 绘制结果 导入后&#xff0c;用户可以选择它进行打印。请参阅下面的示例。要点&#xff1a;确保从 Solution 中选择它。

es-核心储存原理介绍

原始数据 idusernamegradedescription1ahua87i like study2xiaowang92i like es3zhaoyun63i like java 倒排索引 description使用的text分词&#xff0c;使用倒排索引 termidi1,2,3like1,2,3study1es2java3 分词后&#xff0c;如果匹配 es&#xff0c;则需要逐行匹配&…

jmeter中监控服务器ServerAgent

插件下载&#xff1a; 将ServerAgent上传至需要监控的服务器&#xff0c;mac/liunx启动startAgent.sh&#xff08;启动命令&#xff1a;./startAgent.sh&#xff09; 在jmeter中添加permon监控组件 配置需要监控的服务器IP地址&#xff0c;添加需要监控的资源 注意&#xf…

UML 状态图:以共享汽车系统状态图为例

目录 一、初识 UML 状态图 二、共享汽车系统状态图详解 &#xff08;一&#xff09;初始状态与车辆空闲状态 &#xff08;二&#xff09;用户预定相关状态 &#xff08;三&#xff09;等待取车与用户取车状态 &#xff08;四&#xff09;用户还车及后续状态 三、状态图绘…

橙子果品分级-目标检测数据集(包括VOC格式、YOLO格式)

橙子果品分级-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1jpdrylu06mm0r9pGVyb-AQ?pwd94a6 提取码: 94a6 数据集信息介绍&#xff1a; 共有 9195 张图像和一一对应的标注文件 标注文件格式…

uniapp 仿企微左边公司切换页

示例代码&#xff1a; <template><view class"container"><!-- 遮罩层 --><view class"mask" v-if"showSidebar" click"closeSidebar"></view><!-- 侧边栏 --><view class"sidebar"…