GPIO相关介绍

2023-05-16

文章目录

  • GPIO概念
    • TXD与RXD
  • GPIO的使用注意
    • STM32IO口哪些兼容5V
    • 一定不要接超过5V的电压
    • 默认不能做输出的GPIO
  • GPIO硬件
    • 原理图
    • GPIO地址
  • GPIO的八种工作模式
    • 浮空输入
    • 带上拉输入
    • 带下拉输入
    • 模拟输入
    • 开漏输出
    • 推挽输出
    • 复用功能的推挽输出
    • 复用功能的开漏输出
  • STM32F407的GPIO寄存器
    • GPIOx_MODER端口模式寄存器
      • 1-0 ModeRy 输入还是输出
    • GPIOx_OTYPER端口输出类型寄存器
      • 15-0 OTy端口配置
    • GPIOx_OSPEEDR端口输出速度寄存器
    • OSPEEDRY 输出速度
  • STM32F103的GPIO的寄存器
    • 寄存器总览
    • CRL和CRH寄存器
    • IDR寄存器
    • ODR寄存器
    • BSRR寄存器
    • BRR寄存器
    • LCKR寄存器
    • 端口低配置寄存器 CRL
  • GPIO相关库函数
    • GPIO某一位赋值与清零
    • GPIO_SetBits/ResetBits
    • PBin() 和PBoutn()
    • GPIO_ReadInputDataBit()
  • GPIO初始化配置
    • 使能IO口时钟
      • F103时钟使能
      • F407时钟使能
    • GPIO初始化
      • F103GPIO初始化
        • GPIO_Pin
        • GPIO_Mode
        • GPIO_Speed
      • F407GPIO初始化
        • GPIO_Mode
        • GPIO_Speed
        • GPIO_OType
        • GPIO_PuPd
    • 解析F103的GPIO_Init()
  • 端口复用
    • STM32F103端口复用
      • 重映射
        • AFIO时钟
    • STM32F407端口复用
      • 复用器
      • GPIOx_AFRL 寄存器位描述:
      • 407端口复用
        • GPIO_PinAFConfig
  • LED实验设置
    • LED硬件连接图
    • 读取输入电平
    • 读取输出电平
    • 设置输出电平
    • 初始化IO代码
    • 主函数

GPIO概念

General-purpose input/output通用型输入输出接口,类似于51的p0-p3
GPI 通用输入
GPO 通用输出,通过寄存器来控制其输入输出的电平。

在Stm32F103ZET6中,有7组IO,每组16个GPIOA到GPIOG,GPIOA0到GPIOA15.共112个。
每个IO口还有复用功能。
在这里插入图片描述

在Stm32F407中,STM32F407ZGT6

  • 一共有7组IO口,GPIOA到GPIOG。
  • 每组IO口有16个IO
  • 一共16X7=112个IO
  • 外加2个PH0和PH1

一共114个IO口

TXD与RXD

Transmit external data 发送端口 PA9
Receive external data 接收端口 PA10

GPIO的使用注意

STM32IO口哪些兼容5V

凡是数据手册中引脚描述,标有FT的标志就是兼容5V

一定不要接超过5V的电压

不要直接用IO口驱动感性负载(电机,继电器等),因为断开瞬间会产生很大的反电动势,将IO烧坏。(可以接一个泄放二极管)

默认不能做输出的GPIO

默认的五个口不能作为IO口输出

在这里插入图片描述

只有禁止了相应的端口,才能释放IO引脚

GPIO硬件

原理图

在这里插入图片描述
注释:I/O引脚:接收外部发出的信号或者向外部发出信号。

GPIO地址

在这里插入图片描述
GPIOA 的 7 个寄存器都是 32 位的,所以每个寄存器占有 4个地址,一共占用 28 个地址,地址偏移范围为(000h~01Bh)。
因为 GPIO 都是挂载在 APB2 总线之上,所以它的基地址是由 APB2 总线的基地址+GPIOA 在 APB2 总线上的偏移地址决定的。
打开 stm32f10x.h 定位到 GPIO_TypeDef 定义处:

typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;

然后定位到:

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

可以看出,GPIOA 是将 GPIOA_BASE 强制转换为 GPIO_TypeDef 指针,
GPIOA 指向地址 GPIOA_BASE,GPIOA_BASE 存放的数据类型为 GPIO_TypeDef。
查看 GPIOA_BASE的宏定义:

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)

依次类推,可以找到最顶层:

#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define PERIPH_BASE ((uint32_t)0x40000000)

所以我们便可以算出 GPIOA 的基地址位:

GPIOA_BASE= 0x40000000+0x10000+0x0800=0x40010800

GPIOA 的各个寄存器对于 GPIOA 基地址的偏移地址,所以我们自然可以算出来每个寄存器的地址。
GPIOA 的寄存器的地址=GPIOA 基地址+寄存器相对 GPIOA 基地址的偏移值
那就是结构体存储的成员他们的地址是连续的
在这里插入图片描述

GPIO的八种工作模式

浮空输入

浮空输入GPIO_IN_FLOATING,可以做KEY识别,RX1。

带上拉输入

GPIO_IPU——IO内部上拉电阻输入。

带下拉输入

GPIO_IPD—— IO内部下拉电阻输入。

模拟输入

GPIO_AIN ——应用ADC模拟输入,或者低功耗下省电。

开漏输出

输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)。
在这里插入图片描述

GPIO_OUT_OD ——IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现C51的IO双向功能。

开漏输出和推挽输出的区别最普遍的说法就是开漏输出无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动。

  • 开漏输出的这一特性一个明显的优势就是可以很方便的调节输出的电平,因为输出电平完全由上拉电阻连接的电源电平决定。所以在需要进行电平转换的地方,非常适合使用开漏输出。
  • 开漏输出的这一特性另一个好处在于可以实现"线与"功能,所谓的"线与"指的是多个信号线直接连接在一起,只有当所有信号全部为高电平时,合在一起的总线为高电平;只要有任意一个或者多个信号为低电平,则总线为低电平。而推挽输出就不行,如果高电平和低电平连在一起,会出现电流倒灌,损坏器件。所以总线一般会使用开漏输出.
    在这里插入图片描述

推挽输出

在这里插入图片描述

GPIO_OUT_PP ——IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的。
推挽输出结构是由两个MOS或者三极管受到互补控制的信号控制,两个管子时钟一个在导通,一个在截止。在这里插入图片描述
推挽输出的最大特点是可以真正能真正的输出高电平和低电平,在两种电平下都具有驱动能力。

  • 所谓的驱动能力,就是指输出电流的能力。对于驱动大负载(即负载内阻越小,负载越大)时,例如IO输出为5V,驱动的负载内阻为10ohm,于是根据欧姆定律可以正常情况下负载上的电流为0.5A(推算出功率为2.5W)。一般的IO不可能输出这么大的电流。于是造成的结果就是输出电压会被拉下来,达不到标称的5V。
  • 推挽输出高低电平的电流都能达到几十mA。

推挽输出的缺点是,如果当两个推挽输出结构相连在一起,一个输出高电平,另一个输出低电平,电流会从第一个引脚的VCC通过上端MOS再经过第二个引脚的下端MOS直接流向GND,也就是会发生短路,进而可能造成端口的损害。这也是为什么推挽输出不能实现" 线与"的原因。

  • 推挽输出在输出的时候是通过单片机内部的电压,所以他的电压是不能改变的。
  • 一般情况下,使用推挽输出。

复用功能的推挽输出

GPIO_AF_PP ——片内外设功能(I2C的SCL,SDA)。

复用功能的开漏输出

GPIO_AF_OD——片内外设功能(TX1,MOSI,MISO.SCK.SS)。

STM32F407的GPIO寄存器

10个寄存器可以控制一组GPIO的16个IO口。

一个端口模式寄存器(GPIOx_MODER)
一个端口输出类型寄存器(GPIOx_OTYPER)
一个端口输出速度寄存器(GPIOx_OSPEEDR)
一个端口上拉下拉寄存器(GPIOx_PUPDR)
一个端口输入数据寄存器(GPIOx_IDR)
一个端口输出数据寄存器(GPIOx_ODR)
一个端口置位/复位寄存器(GPIOx_BSRR)
一个端口配置锁存寄存器(GPIOx_LCKR)
两个复用功能寄存器(低位GPIOx_AFRL & GPIOx_AFRH)
在这里插入图片描述

GPIOx_MODER端口模式寄存器

x = A…I
在这里插入图片描述

1-0 ModeRy 输入还是输出

在这里插入图片描述

GPIOx_OTYPER端口输出类型寄存器

x = A…I
中文参考手册7.4.2,187
在这里插入图片描述

15-0 OTy端口配置

在这里插入图片描述

GPIOx_OSPEEDR端口输出速度寄存器

GPIO port output speed register
在这里插入图片描述

OSPEEDRY 输出速度

在这里插入图片描述

STM32F103的GPIO的寄存器

寄存器总览

在这里插入图片描述

IO 口寄存器必须要按 32 位字被访问。
STM32 的每个 IO 端口都有 7 个寄存器来控制。

CRL和CRH寄存器

配置模式的 2 个 32 位的端口配置寄存器 CRL 和 CRH;
端口低配置寄存器 CRL
CRL 和 CRH 控制着每个 IO 口的模式及输出速率。
2 个 32 位的数据寄存器 IDR 和 ODR;

IDR寄存器

IDR 是一个端口输入数据寄存器,只用了低 16 位。
该寄存器为只读寄存器,并且只能以
16 位的形式读出。
在这里插入图片描述

要想知道某个 IO 口的电平状态,你只要读这个寄存器,再看某个位的状态就可以了。
在固件库中操作 IDR 寄存器读取 IO 端口数据是通过 GPIO_ReadInputDataBit 函数实现的:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
比如我要读 GPIOA.5 的电平状态,那么方法是:
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);
返回值是 1(Bit_SET)或者 0(Bit_RESET);

ODR寄存器

ODR 是一个端口输出数据寄存器,也只用了低 16 位。
该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前 IO 口的输出状态。
向该寄存器写数据,则可以控制某个 IO 口的输出电平。
在这里插入图片描述

在固件库中设置 ODR 寄存器的值来控制 IO 口的输出状态是通过函数 GPIO_Write 来实现的:
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
该函数一般用来往一次性一个 GPIO 的多个端口设值。

BSRR寄存器

1 个 32 位的置位/复位寄存器BSRR;
BSRR 寄存器是端口位设置/清除寄存器。该寄存器和 ODR 寄存器具有类似的作用,都可以用来设置 GPIO 端口的输出位是 1 还是 0。
在这里插入图片描述

如你要设置 GPIOA 的第 1 个端口值为 1,那么你只需要往寄存器 BSRR 的低 16 位对应位写 1 即可:
GPIOA->BSRR=1<<1;
如果你要设置 GPIOA 的第 1 个端口值为 0,你只需要往寄存器高 16 位对应为写 1 即可:
GPIOA->BSRR=1<<(16+1)//清除第十七位的内容。
该寄存器往相应位写 0 是无影响的,所以我们要设置某些位,我们不用管其他位的值。
在 STM32 固件库中,通过 BSRR 和 BRR 寄存器设置 GPIO 端口输出是通过函数GPIO_SetBits()和函数 GPIO_ResetBits()来完成的
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
比如我们要设
置 GPIOB.5 输出 1,那么方法为:
GPIO_SetBits(GPIOB, GPIO_Pin_5);
反之如果要设置 GPIOB.5 输出位 0,方法为:
GPIO_ResetBits (GPIOB, GPIO_Pin_5);
也可以通过直接操作寄存器
BRR 和 BSRR 的方式来操作 IO 口输出高低电平,方法如下:
GPIOB->BRR=GPIO_Pin_5; //设置 GPIOB.5 输出 1,等同 LED0=1;
GPIOB->BSRR=GPIO_Pin_5; //设置 GPIOB.5 输出 0,等同 LED0=0;

BRR寄存器

Port bit reset register(GPIOx_BRR)32位寄存器,高16位保留,重置。
BRR 寄存器是端口位清除寄存器。该寄存器的作用跟 BSRR 的高 16 位雷同。

LCKR寄存器

1 个 32 位的锁存寄存器 LCKR。

端口低配置寄存器 CRL

GPIOA这一组,有GPIOA0~GPIOA15一共16个IO口。每一个IO口需要寄存器的4位用来配置工作模式,两位配置位,两位工作模式位。
那么一组GPIO就需要16x4=64位的寄存器来存放这一组GPIO的工作模式的配置,但STM32的寄存器都是32位的,所以只能使用2个32位的寄存器来存放了。CRL用来存放低八位的IO口(GPIOx0—GPIOx7)的配置,CRH用来存放高八位的IO口(GPIOx8—GPIOx15)的配置。
在这里插入图片描述
在这里插入图片描述
STM32 的 CRL 控制着每组 IO 端口(A~G)的低 8 位的模式。每个 IO 端口的位占用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。

CRH 的作用和 CRL 完全一样,只是 CRL 控制的是低 8 位输出口,而 CRH 控制的是高 8位输出口。

GPIO相关库函数

GPIO某一位赋值与清零

可以先对寄存器的值进行&清零操作
GPIOA->CRL&=0XFFFFFF0F; //将第 4-7 位清 0
然后再与需要设置的值进行|或运算
GPIOA->CRL|=0X00000040; //设置相应位的值,不改变其他位的值

将 BSRR 寄存器的第 pinpos 位设置为 1,1左移了pinpos位
GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
可以提高代码的可读性以及可重用性。
类似这样的代码很多:
GPIOA->ODR|=1<<5; //PA.5 输出高,不改变其他位

GPIO_SetBits/ResetBits

实现功能:控制某个GPIO引脚的输出电平(拉高 / 拉低)
GPIO_SetBits   拉高引脚输出电平
GPIO_ResetBits 拉低引脚输出电平

PBin() 和PBoutn()

顾名思义

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

GPIO_ReadInputDataBit()

通过调用函数 GPIO_ReadInputDataBit()来读取 IO 口的状态的。

实现通过4 个按钮(WK_UP、KEY0、KEY1 和 KEY2),来控制板上的 2 个 LED(DS0 和 DS1)和蜂鸣器
其中 WK_UP 控制蜂鸣器,按一次叫,再按一次停;
KEY2 控制 DS0,按一次亮,再按一次灭;
KEY1 控制 DS1,效果同KEY2;
KEY0 则同时控制 DS0 和 DS1,按一次,他们的状态就翻转一次。
按键与端口的对应关系见(按键)

按键初始化

#include "key.h"
#include "sys.h"
#include "delay.h"
//按键初始化函数
void KEY_Init(void) //IO 初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|
RCC_APB2Periph_GPIOE,ENABLE); //使能 PORTA,PORTE 时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//GPIOE.2~4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化 GPIOE2,3,4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //初始化 WK_UP-->GPIOA.0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 设置成输入,下拉
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下;1,KEY0 按下;2,KEY1 按下;3,KEY2 按下 ;4,KEY3 按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>KEY3!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1; //按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1))
{
delay_ms(10); //去抖动
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(KEY2==0)return KEY2_PRES;
else if(KEY3==1)return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==0)key_up=1;
return 0; // 无按键按下
}

KEY_Scan()函数,则是用来扫描这 4 个 IO 口是否有按键按下。
KEY_Scan()函数,支持两种扫描方式,通过 mode 参数来设置。
当 mode 为 0 的时候,KEY_Scan()函数将不支持连续按,扫描某个按键,该按键按下之后必须要松开,才能第二次触发,否则不会再响应这个按键,这样的好处就是可以防止按一次多次触发,而坏处就是在需要长按的时候比较不合适。
当 mode 为 1 的时候,KEY_Scan()函数将支持连续按,如果某个按键一直按下,则会一直返回这个按键的键值,这样可以方便的实现长按检测。
要注意的就是,该函数的按键扫描是有优先级的,最优先的是 KEY0,第二优先的是 KEY1,接着 KEY2,最后是 WK_UP 按键。
该函数有返回值,如果有按键按下,则返回非 0 值,如果没有或者按键不正确,则返回 0。

头文件 key.h 里面的代码:

#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键 0
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键 1
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)//读取按键 2
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键 3(WK_UP)
#define KEY0_PRES 1 //KEY0 按下
#define KEY1_PRES 2 //KEY1 按下
#define KEY2_PRES 3 //KEY2 按下
#define WKUP_PRES 4 //WK_UP 按下(即 WK_UP/WK_UP)
void KEY_Init(void); //IO 初始化
u8 KEY_Scan(u8); //按键扫描函数
#endif

主函数的代码

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "beep.h"
//ALIENTEK 战舰 STM32 开发板实验 3
//按键输入实验
int main(void)
{
u8 key;
delay_init(); //延时函数初始化
LED_Init(); //LED 端口初始化
KEY_Init(); //初始化与按键连接的硬件接口
BEEP_Init(); //初始化蜂鸣器端口
LED0=0; //先点亮红灯
while(1)
{
key =KEY_Scan(0); //得到键值
if(key)
{ switch(t)
{ case WKUP_PRES: //控制蜂鸣器
	BEEP=!BEEP;break;
	case KEY2_PRES: //控制 LED0 翻转
	LED0=!LED0;break;
	case KEY1_PRES: //控制 LED1 翻转
	LED1=!LED1;break;
	case KEY0_PRES: //同时控制 LED0,LED1 翻转
	LED0=!LED0;
	LED1=!LED1;break;
}
}else delay_ms(10);
}
}

在仿真调试的时候MDK 不会考虑 STM32 自带的上拉和下拉,所以我们得自己手动设置一下,来使得其初始状态和外部硬件的状态一摸一样。
在 General Purpose I/O E 窗口内的 Pins 里面勾选 2、3、4 位,

要改变状态就把 Pins 的 PE2 取消勾选,再次执行过这句,得到 key 的值为 3

GPIO初始化配置

使能IO口时钟

这里需要注意的是:在配置 STM32 外设的时候,任何时候都要先使能该外设的时钟。

F103时钟使能

在 stm32f10x.h 文件里面我们可以看到如下的宏定义:

#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010)
#define RCC_APB1Periph_TIM2 ((uint32_t)0x00000001)
#define RCC_APB1Periph_TIM3 ((uint32_t)0x00000002)
#define RCC_APB1Periph_TIM4 ((uint32_t)0x00000004)
#define RCC_AHBPeriph_DMA1 ((uint32_t)0x00000001)
#define RCC_AHBPeriph_DMA2 ((uint32_t)0x00000002)

可以很明显的看出

  • GPIOA~GPIOC 是挂载在 APB2 下面,在使能 GPIO 的时候调用的是 RCC_APB2PeriphResetCmd()函数使能。
  • TIM2~TIM4 是挂载在 APB1下 面
  • DMA 是 挂 载 在 AHB 下 面 。 所 以 在 使 能 DMA 的 时 候 记 住 要 调 用 的 是RCC_AHBPeriphClock()函数使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能 GPIOB,GPIOE 端口时钟

这行代码的作用是使能 APB2 总线上的 GPIOB 和 GPIOE 的时钟。

F407时钟使能

F407的挂载和F103是有差别的。 可以先参考407系统上挂载的图。
官方库提供了五个打开 GPIO 和外设时钟的函数分别为:

void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_AHB2PeriphClockCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
void RCC_AHB3PeriphClockCmd(uint32_t RCC_AHB3Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

F407中GPIO是挂载在AHB下的。

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

GPIO初始化

GPIO的每个 IO 口可以自由编程,但 IO 口寄存器必须要按 32 位字被访问。

F103GPIO初始化

  • 在固件库中,GPIO 端口操作对应的库函数函数以及相关定义在文件stm32f10x_gpio.h 和 stm32f10x_gpio.c 中。
  • 操作寄存器 CRH 和 CRL 来配置 IO 口的模式和速度是通过 GPIO 初始化函数完成:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

第一个参数是用来指定 GPIO,取值范围为 GPIOA~GPIOG。
第二个参数为初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。
通过初始化结构体初始化 GPIO 的常用格式是:

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度 50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);//根据设定参数配置 GPIO

上面代码的意思是设置 GPIOB 的第 5 个端口为推挽输出模式,同时速度为 50M。

GPIO_Pin

第一个成员变量 GPIO_Pin 用来设置是要初始化哪个或者哪些 IO 口;

GPIO_Mode

GPIO_Mode 是用来设置对应 IO 端口的输出输入模式,
这些模式是上面我们讲解的 8 个模式,在 MDK 中是通过一个枚举类型定义的:

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;

GPIO_Speed

第三个参数是 IO 口速度设置,有三个可选值,在 MDK 中同样是通过枚举类型定义:

typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

F407GPIO初始化

typedef struct
{
uint32_t GPIO_Pin;
GPIOMode_TypeDef GPIO_Mode;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOOType_TypeDef GPIO_OType;
GPIOPuPd_TypeDef GPIO_PuPd;
}GPIO_InitTypeDef; 

初始化代码:

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9//GPIOF9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO

GPIO_Mode

F407的GPIO_Mode与F103是有区别的,具体如下

typedef enum
{
	GPIO_Mode_IN = 0x00, /* 复位状态的输入!< GPIO Input Mode */
	GPIO_Mode_OUT = 0x01, /*通用输出模式!< GPIO Output Mode */
	GPIO_Mode_AF = 0x02, /*复用功能模式!< GPIO Alternate function Mode */
	GPIO_Mode_AN = 0x03 /*模拟输入模式!< GPIO Analog Mode */
}GPIOMode_TypeDef;

GPIO_Speed

F407的GPIO_Speed与F103也是有区别的,具体如下

typedef enum
{
GPIO_Low_Speed = 0x00, /*!< Low speed */
GPIO_Medium_Speed = 0x01, /*!< Medium speed */
GPIO_Fast_Speed = 0x02, /*!< Fast speed */
GPIO_High_Speed = 0x03 /*!< High speed */
}GPIOSpeed_TypeDef;
/* Add legacy definition */
#define GPIO_Speed_2MHz GPIO_Low_Speed
#define GPIO_Speed_25MHz GPIO_Medium_Speed
#define GPIO_Speed_50MHz GPIO_Fast_Speed
#define GPIO_Speed_100MHz GPIO_High_Speed

有四个可选值。实际上这就是配置的 GPIO对应的 OSPEEDR 寄存器的值。

GPIO_OType

GPIO_OType是F407新增的,GPIO 的输出类型设置,实际上是配置的 GPIO 的 OTYPER 寄存器的值。

 typedef enum
{
	GPIO_OType_PP = 0x00,
	GPIO_OType_OD = 0x01
}GPIOOType_TypeDef;

输出推挽模式,那么选择值 GPIO_OType_PP。
输出开漏模式,那么设置值为 GPIO_OType_OD。

GPIO_PuPd

F407新增,设置 IO 口的上下拉,实际上就是设置 GPIO 的 PUPDR 寄存器的值。

typedef enum
{
	GPIO_PuPd_NOPULL = 0x00,
	GPIO_PuPd_UP = 0x01,
	GPIO_PuPd_DOWN = 0x02
}GPIOPuPd_TypeDef;
  • GPIO_PuPd_NOPULL 为不使用上下拉
  • GPIO_PuPd_UP 为上拉,
  • GPIO_PuPd_DOWN 为下拉。所谓的下拉就是空闲的时候是低电平。

解析F103的GPIO_Init()

GPIO_Init()函数的实现处,同样,双击 GPIO_Init,右键点击“Go to definition of …”

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
……
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
……
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
……
}

assert_param 函数式对入口参数的有效性进行判断,所以我们可以从这个函数入手,确定我们的入口参数的范围。
第一行是对第一个参数 GPIOx 进行有效性判断,双击“IS_GPIO_ALL_PERIPH”右键点击“go to defition of…” 定位到了下面的定义:

#define IS_GPIO_ALL_PERIPH(PERIPH) (((PERIPH) == GPIOA) || \
((PERIPH) == GPIOB) || \
((PERIPH) == GPIOC) || \
((PERIPH) == GPIOD) || \
((PERIPH) == GPIOE) || \
((PERIPH) == GPIOF) || \
((PERIPH) == GPIOG))

很明显可以看出,GPIOx 的取值规定只允许是 GPIOA~GPIOG。
同样的办法,我们双击“IS_GPIO_MODE” 右键点击“go to defition of…”,定位到下面的定义:

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;
#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || \
((MODE) == GPIO_Mode_IN_FLOATING) || \
((MODE) == GPIO_Mode_IPD) || \
((MODE) == GPIO_Mode_IPU) || \
((MODE) == GPIO_Mode_Out_OD) || \
((MODE) == GPIO_Mode_Out_PP) || \
((MODE) == GPIO_Mode_AF_OD) || \
((MODE) == GPIO_Mode_AF_PP))

所以 GPIO_InitStruct->GPIO_Mode 成员的取值范围只能是上面定义的 8 种。这 8 中模式是通过一个枚举类型组织在一起的。
同样的方法可以找出 GPIO_Speed 的参数限制:

typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
#define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || \
((SPEED) == GPIO_Speed_2MHz) || \
((SPEED) == GPIO_Speed_50MHz))

在这里插入图片描述

同样的方法我们双击“IS_GPIO_PIN” 右键点击“go to defition of…”,定位到下面的定义:

#define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) !=(uint16_t)0x00))

可以看出,GPIO_Pin 成员变量的取值范围为 0x0000 到 0xffff

MDK 会将这些数字的意思通过宏定义定义出来,,看到在 IS_GPIO_PIN(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_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 */
#define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) !=
(uint16_t)0x00))

这些宏定义 GPIO_Pin_0~GPIO_Pin_ All 就是 MDK 事先定义好的于是我们可以组织起来下面的代码:

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
初始化多个 IO 口的方式可以是如下:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7; //指定端口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //端口模式:推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化

端口复用

每个GPIO口有很多功能,默认的是作为普通IO口,此时不配置复用相关的东西。

  • 当GPIO作为其他功能时,这个时候就要配置复用相关代码了

复用:内置外设基本上与I/O口共用管脚的,也就是I/O管脚的复用。

  • 比如串口 1 的引脚对应的 IO 为 PA9,PA10.PA9,PA10 默认功能是 GPIO,所以当PA9,PA10 引脚作为串口 1 的 TX,RX 引脚使用的时候,那就是端口复用。

内置外设:
除单片机内核外,单片机内部可实现功能的设备都是内置外设,如串口,ADC 等。

区别:

  • stm32f1的外设有默认引脚,当使用默认引脚时就不需要使用AFIO的重映射功能,如果需要重映射就使用。
  • tm32f4的所有外设没有默认引脚,当使用外设时必须要使用GPIO_PinAFConfig函数选择引脚进行端口复用。
  • stm32f1的外部中断与其他外设的端口复用都要用AFIO,而stm32f4的外部中断与其他外设分开了,外部中断使用SYSCFG进行操作,外设端口选择使用GPIO_PinAFConfig函数。
    • stm32f4的SYSCFG有独立的时钟,而stm32f4的AF没有独立时钟。
  • 当使用AD时,选择模拟模式而不是复用(特殊)。

STM32F103端口复用

I/O口不是随意复用,而是有专门的映射关系:
在这里插入图片描述

先使能IO端口时钟,再使能外设,也就是串口时钟,然后配置端口模式。
1) GPIO 端口时钟使能。
要使用到端口复用,当然要使能端口的时钟了。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

2) 复用的外设时钟使能。
比如你要将端口 PA9,PA10 复用为串口,所以要使能串口时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

3) 端口模式配置。
在 IO 复用位内置外设功能引脚的时候,必须设置 GPIO 端口的复用模式。

这里我们拿 Usart1 举例
在这里插入图片描述

我们要配置全双工的串口 1,那么 TX 管脚需要配置为推挽复用输出,RX 管脚配置为浮空输入或者带上拉输入。

//USART1_TX PA.9 复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);

重映射

很多复用内置的外设的I/O引脚可以通过重映射功能,从不同的I/O管脚引出。但只能映射到固定的管脚。
使用顺序:

  • 使能GPIO
  • 使能外设
  • 使能AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

该语句打开了GPIOD重映射功能。该功能复用重映射和调试 IO 配置寄存器(AFIO_MAPR)控制。

  • 开启重映射
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);

部分重映射

  • 功能外设的部分引脚重新映射,还有一部分引脚是原来的默认引脚

完全重映射

  • 功能外设的所有引脚都重新映射
    在这里插入图片描述

AFIO时钟

AFIO_MAPR:配置复用功能重映射
AFIO_EXTICRX:配置外部中断线映射
AFIO_EVCR: 配置EVENTOUT事件输出
三个寄存器及AFIO的详情

STM32F407端口复用

复用器

STM32F4 系列微控制器 IO 引脚通过一个复用器连接到内置外设或模块。

  • 该复用器一次只允许一个外设的复用功能(AF)连接到对应的 IO 口。这样可以确保共用同一个 IO 引脚的外设之间不会发生冲突。
  • 每个 IO 引脚(如GPIOA_Pin0)都有一个复用器,该复用器采用 16 路复用功能输入(AF0 到 AF15),可通过GPIOx_AFRL(针对引脚 0-7)和 GPIOx_AFRH(针对引脚 8-15)寄存器对这些输入进行配置,每四位控制一路复用:
    1)完成复位后,所有 IO 都会连接到系统的复用功能 0(AF0)。
    2)外设的复用功能映射到 AF1 到 AF13。
    3)Cortex-M4 EVENTOUT 映射到 AF15。
    复用器的图:
    在这里插入图片描述

GPIOx_AFRL 寄存器位描述:

在这里插入图片描述
32 位寄存器 GPIOx_AFRL 每四个位控制一个 IO 口,所以每个寄存器控制32/4=8 个 IO 口。寄存器对应四位的值配置决定这个 IO 映射到哪个复用功能 AF。

配置 GPIOx_AFRL 或者 GPIOx_AFRH 寄存器

407端口复用

同样的,串口1的发送接收引脚是PA9,PA10。当我们把PA9,PA10不用做GPIO,而用做复用功能串口1的发送接收引脚的时候,叫端口复用。

  • USART2_RX串口2除了PA2,PA3。PD6,PD5也有USART2_RX的功能。

使用配置:

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
 
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //*PA9 连接 AF7,复用为 USART1_TX */
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); ///* PA10 连接 AF7,复用为 USART1_RX*/
	
	//USART1端口配置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10

GPIO_PinAFConfig

  • 使用GPIO_PinAFConfig函数进行指定引脚的挂载。除ADC和DAC外的所有非GPIO功能,都要使用GPIO_PinAFConfig函数进行端口复用
    • 端口复用没有时钟,直接使用即可。
    • 作用就是将 IO 连接到所需的 AFx,AF代表的就是端口复用

复用外设选择:

#define IS_GPIO_AF(AF) (((AF) == GPIO_AF_RTC_50Hz) ||((AF) == GPIO_AF_TIM14) || \
((AF) == GPIO_AF_MCO) || ((AF) == GPIO_AF_TAMPER) || \
((AF) == GPIO_AF_SWJ) || ((AF) == GPIO_AF_TRACE) || \
((AF) == GPIO_AF_TIM1) || ((AF) == GPIO_AF_TIM2) || \
((AF) == GPIO_AF_TIM3) || ((AF) == GPIO_AF_TIM4) || \
((AF) == GPIO_AF_TIM5) || ((AF) == GPIO_AF_TIM8) || \
((AF) == GPIO_AF_I2C1) || ((AF) == GPIO_AF_I2C2) || \
((AF) == GPIO_AF_I2C3) || ((AF) == GPIO_AF_SPI1) || \
((AF) == GPIO_AF_SPI2) || ((AF) == GPIO_AF_TIM13) || \
((AF) == GPIO_AF_SPI3) || ((AF) == GPIO_AF_TIM14) || \
((AF) == GPIO_AF_USART1) || ((AF) == GPIO_AF_USART2) || \
((AF) == GPIO_AF_USART3) || ((AF) == GPIO_AF_UART4) || \
((AF) == GPIO_AF_UART5) || ((AF) == GPIO_AF_USART6) || \
((AF) == GPIO_AF_CAN1) || ((AF) == GPIO_AF_CAN2) || \
((AF) == GPIO_AF_OTG_FS) || ((AF) == GPIO_AF_OTG_HS) || \
((AF) == GPIO_AF_ETH) || ((AF) == GPIO_AF_OTG_HS_FS) || \
((AF) == GPIO_AF_SDIO) || ((AF) == GPIO_AF_DCMI) || \
((AF) == GPIO_AF_EVENTOUT) || ((AF) == GPIO_AF_FSMC))

LED实验设置

LED硬件连接图

在这里插入图片描述
当输出1的时候无电压差,灭。
当输出0 的时候有压差,亮。

应该设置推挽输出,上拉,默认就是灭。

读取输入电平

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

第一个作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平

第二个作用:读取某组GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。
GPIO_ReadInputData(GPIOA);//读取GPIOA组中所有io口输入电平

读取输出电平

 uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

作用:读取某个GPIO的输出电平。实际操作的是GPIO_ODR寄存器。
GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平

作用:读取某组GPIO的输出电平。实际操作的是GPIO_ODR寄存器。
GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平

设置输出电平

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

作用:设置某个IO口输出为高电平(1)。实际操作BSRRL寄存器

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

作用:设置某个IO口输出为低电平(0)。实际操作的BSRRH寄存器。

void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

这两个函数不常用,也是用来设置IO口输出电平。

初始化IO代码

void LED_Init(void)
{    	 
  GPIO_InitTypeDef  GPIO_InitStructure;

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

  //GPIOF9,F10初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
	
	GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10设置高,灯灭

}

主函数

int main(void)
{ 
 
	delay_init(168);		  //初始化延时函数
	LED_Init();		        //初始化LED端口
	
  /**下面是通过直接操作库函数的方式实现IO控制**/	
	
	while(1)
	{
	GPIO_ResetBits(GPIOF,GPIO_Pin_9);  //LED0对应引脚GPIOF.9拉低,亮  等同LED0=0;
	GPIO_SetBits(GPIOF,GPIO_Pin_10);   //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
	delay_ms(500);  		   //延时300ms
	GPIO_SetBits(GPIOF,GPIO_Pin_9);	   //LED0对应引脚GPIOF.0拉高,灭  等同LED0=1;
	GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
	delay_ms(500);                     //延时300ms
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GPIO相关介绍 的相关文章

  • java 优化双重for循环

    首先我们要有两个对象分别是 学生信息 和 学生住宿信息 span class token keyword class span span class token class name Student span span class toke
  • 微服务 - gateway网关配置

    server port 10010 网关端口 spring application name gateway 服务名称 cloud nacos server addr localhost 8848 nacos地址 gateway route
  • 如何在手机或平板上编写代码?

    下面给大家推荐一款免费的 在线协作式 基于浏览器的 IDE的在线编程网站 支持语言包括 Java C 43 43 C C JavaScript CSS PHP等50多种主流开发语言 地址 The collaborative browser
  • 羊了个羊, 低配版开源代码来啦~

    前几天朋友圈突然被一个小游戏 羊了个羊 刷屏了 xff0c 出于好奇我也打算小玩一把试试 xff0c 结果没想到上头了 游戏的玩法非常简单 xff0c 类似 消消乐 xff0c 从一堆方块中找到相同图案的 3 个方块并消除即可 但没想到 x
  • MySQL 使用索引和不使用索引的区别(附17W条数据SQL文件)

    MySQL 使用索引可以减少查询的时间 xff0c 而不使用索引的查询会更加耗时 xff0c 因为MySQL需要扫描整个表 此外 xff0c 使用索引可以提高查询的性能 xff0c 同时也可以提高查询的可读性和可维护性 换句话来说 使用索引
  • 如何使用AI来帮你写代码(Cursor使用教程)

    x1f4ac 产品介绍 cursor是一个新的Ide xff0c 它使用Ai来帮助您重构理解调试并使用Cursor编写代码我们的目标是使构建软件的过程更快 更愉快 我们从头开始构建了一个代码编辑器 xff0c 对我们的第一个功能进行了原型设
  • [Java多线程-基础] 如何定位线程中的死锁问题?

    x1f512 死锁代码 下面提供的代码演示了死锁的情况 程序创建了两个线程 xff0c 线程1和线程2 xff0c 它们都试图以不同的顺序获取两个不同的资源 xff0c resource1和resource2 线程1首先获取resource
  • [Java多线程-基础] 避免线程死锁问题(ReentrantLock的使用)

    ReentrantLock 的设计初衷是为了提供一种比 synchronized 更加灵活和可控的锁机制 与 synchronized 相比 xff0c ReentrantLock 提供了更多的功能 xff0c 如可重入性 公平锁和中断锁等
  • IDEA插件:智能代码生成器,附带注释和性能/安全检测功能

    x1f680 1 安装插件 在插件中搜索关键字 biot 点击安装 x1f680 2 代码生成 右侧的侧边栏点击biot后 在下方的输入框中输入你要问的内容 x1f680 3 biot AI 选中选区中的代码 点击鼠标右键让ai来帮你改代码
  • 安装Windows Server 2016 服务器 标准版

    注意事项 xff1a 安装带桌面版的 管理员密码设置 xff0c 要 注意大小写加数字 xff0c 不然会设置失败 安装文件下载 xff1a MSDN 我告诉你 PE U盘 微PE 服务器的驱动 xff0c 可以自己到对应服务器厂家的官网上
  • 第五节:基于Pytorch的相关可视化

    第五节 xff1a 基于Pytorch的相关可视化 在Pytorch发布后 xff0c 网络及训练过程的可视化工具也相应的被开发出来来帮助用户监督所建立的模型的结构和训练过程 本章将讲解HiddenLayer库 xff0c HiddenLa
  • 第六节:Pytorch实现全连接神经网络

    第六节 xff1a Pytorch实现全连接神经网络 前面的五节中 xff0c 我们讲解了使用PyTorch搭建一个神经网络中需要的需要各种技巧 xff0c 包括 xff1a 网络的搭建 选择不同的实践技巧 xff08 优化器选择 学习率下
  • 使用Visual Studio Code开发Arduino踩坑日记(持续更新)

    使用Visual Studio Code开发Arduino踩坑日记 持续更新 文章目录 使用Visual Studio Code开发Arduino踩坑日记 持续更新 1 在browse path中未找到包含文件问题描述问题分析解决思路解决过
  • 小白安装Ubuntu 18.04 LTS

    文章目录 小白安装Ubuntu 18 04 LTS作者 xff1a 王仕鸿日期 xff1a 2020 10 10 前言 xff08 可跳过 xff09 Ubuntu介绍操作系统介绍Ubuntu介绍 安装Ubuntu 18 04 LTS步骤一
  • 1_ROS基础

    ROS基础 本章讲解ROS中最基础的概念 不明白这些概念是没法学懂ROS的 学习了这些概念 后面我们将通过实操来在实践的过程中进一步体会 ROS是什么 ROS Robot Operating System 机器人操作系统 是一个提供一系列程
  • 2_ROS中的命令行工具

    ROS中的命令行工具 ROS中为我们提供了丰富的命令行工具 帮助我们进行代码的编写 调试 测试 框架的搭建 数据的显示等等 大图如下 所有的命令大致可以分为四类 分别是运行相关命令 编译相关命令 包制作管理相关命令 项目创建相关命令 下面进
  • 3_ROS创建工作空间和功能包

    3 ROS创建工作空间和功能包 前面我们讲解了ROS中的核心概念和使用ROS进行开发时候必须用到的命令行工具 下面我们就正式开始ROS中的开发 我们首先从创建工作空间和功能包开始 1 工作空间WorkSpace 工作空间是ROS中非常重要的
  • 4_Publieher的编程实现

    4 Publisher的编程实现 我们前面讲解了如何创建工作空间和功能包 但是我们都仅仅只创建了一个空的工作空间和功能包 什么都没有实现 我们想要进一步为功能包添加功能 就不可避免的需要添加Publisher和Subscriber 下面我们
  • 1.Latex介绍

    Latex介绍 本人鸿神 目前就读于XJTU 是一个即将开始科研的小白 既然做科研未来就无法避免发表论文 而发表论文就需要用到一系列的工具 Latex就是其中之一 谨以此文记录我的科研路 也希望Latex这一系列文章能够帮到各位 1 什么是
  • 2.Latex安装和TeXworks Editor基础

    二 Latex安装和TeXworks Editor使用教程 上一章我们讲解了什么是Latex和为什么我们要学习Latex 从这一章开始我们就要正式开始学习Latex 就像前面所讲的 Latex包含编译器和编辑器 我们需要在编辑器中编写夹杂代

随机推荐

  • 关于“ErrorFlash Download failed“Cortex-M3”的解决办法

    首先 xff0c 将仿真器连接电脑 xff0c 然后打开KEIL xff0c 点击FLash gt Erase xff0c 擦除Flash试一下 如果擦除不成功 xff0c 那么应该是的STM32的Flash被锁了 xff0c 要解锁一下
  • 3.Latex语法基础:命令与环境

    三 Latex语法基础 命令与环境 前面我们已经做好了开始编写Latex的一切准备工作 从这章开始 我们将开始讲解Latex语法 本章将讲解Latex语法的基础 命令与环境 1 命令与环境 命令 什么是命令 不同于其他编程语言 C C 43
  • Arduino多种传感器使用方法

    Arduino项目 智能窗户 前段时间参加了一个Arduino的比赛 具体内容就是用Arduino设计一个项目出来 我在的队伍的设计的项目就是智能窗户 智能窗户可以采集本地传感器采集到的环境参数 根据参数具有一套内部的逻辑判断 可以对温度
  • Paxos算法

    Paxos算法 Paxos算法是一系列共识算法中的一个 其目的就是为了解决共识 一致性问题 这个Github连接中详细的列出了多种共识算法 还有一些工程实践的例子 腾讯 Zookeeper Handpoo下的一个分布式框架 Handoop是
  • Python中的推导式

    推导式 Compression 推导式 Compression 是Python语言的一大特色 相比于其他语言而言 推导式使得Python能够编辑的进行循环 创建出特定的字典 列表等可迭代对象 使用推导式可以避免代码的冗长 简化代码风格 使得
  • A* 算法 Python实现

    A 算法 Python实现 最近考试周 没时间封装的很好 代码分两部分 定义和调用部分 定义部分 span class token keyword import span numpy span class token keyword as
  • Pytorch中使用TensorBoard

    本文记录了如何在Pytorch中使用Tensorboard xff08 主要是为了备忘 xff09 Pytorch中使用TensorBoard 虽然我本身就会用TensorBoard xff0c 但是因为TensorBoard只有在写训练代
  • A*算法-Python实现

    好久没有在CSDN上发文章了 xff0c 快一年了吧 这两天重新登录了一下 xff0c 不看不知道 xff0c 一看吓一跳 xff0c 没想到访问量快13万了 之前写博客的时候 xff0c 想着把一些有用的东西写下来 xff0c 一方面是当
  • 哈夫曼压缩算法-Python实现

    前面上传了A 算法的实现 xff0c 就顺便把一起写的哈夫曼压缩也上传了吧 本文主要提供了Python版本的哈夫曼压缩算法实现 xff0c 并在此基础上提供了命令行和基于Qt的GUI用户界面 xff08 User Interface xff
  • 内存分段与内存分页:逻辑地址、物理地址、线性地址、虚拟地址

    这篇文章也是我自己的博客网站的里的文章 xff0c 我觉得这篇文章还是我觉得知识含量比较高的文章 xff0c 所以特地把它发出来看看 这篇文章写于我在写自己的操作系统JackOS的时候系统梳理了一下CPU访问内存的各种方式 xff0c 写完
  • VSCode调试C/C++项目

    最近写完了自己的操作系统 xff0c 深感有一个方便的调试环境是有多么重要 xff0c 能够提升不少开发效率 恰好最近在的技术交流群里群友在问如何搭建VSCode调试操作系统的环境 xff0c 刚考完试 xff0c 就先把这篇VSCode调
  • 误差与精度

    整理自 误差理论与数据处理 合肥工业大学 机械专业用于教授学生误差与精度概念的课程叫做 公差与测量 或者叫做 机械精度设计 xff0c 而公差或者精度的本质含义就是误差的大小 xff0c 公差越小 xff0c 误差越小 xff0c 精度越高
  • 两个类的头文件互相包含

    两个类的头文件互相包含 我做任务的时候遇到了两个类都互相包含对方的对象的问题 xff0c 本来是有错误的 xff0c 但经过我一番改动 xff0c 两个头文件互相包含同时 xff0c 每个头文件都含有令一个类的前置声明 虽然最后运行正确 x
  • 【C++ STL 容器】——vector

    概述 vector容器也被称作向量 xff0c 实现了动态的数组 xff0c 用于元素数量变化的对象数组 xff0c 算是比较常用的容器 常用函数 构造函数 vector 创建一个空vectorvector int size 创建一个vec
  • 2021-07-22

    MSP432在keil中通过CMSIS DAP下载程序出现cannot enter debug mode的解决办法 xff1a MSP432下载程序出现cannot enter debug mode 可以通过修改如下设置 Debug里面的两
  • 通信协议基础以及常用的串口通信协议

    通信协议 xff1a 串行通信和并行通信 在数据的通信方式中根据数据传输方式的不同可以分为 xff1a 串行通信和并行通信 串行通信 xff1a 串行通信是指使用一条数据线 xff0c 将数据一位一位地依次传输 xff0c 每一位数据占据一
  • Ubuntu安装ROS melodic,管理环境,创建工作空间

    一 安装ROS 1 设置源 xff1a sudo sh c 39 etc lsb release amp amp echo 34 deb http mirrors tuna tsinghua edu cn ros ubuntu 96 lsb
  • HTTP请求报文的结构组成及URL的结构组成

    HTTP请求报文 HTTP 超文本传输协议 Hypertext Transfer Protocol xff0c 简称HTTP 是应用层协议 HTTP 是一种请求 响应式的协议 xff0c 即一个客户端与服务器建立连接后 xff0c 向服务器
  • Qt之旅_001>>Qt常用窗口类之间的关系

    QApplication xff0c QGuiApplication QCoreApplication三者之间的关系 QCoreApplication处于core模块中 xff0c 为应用程序提供了一个非gui的时间循环 xff1b QGu
  • GPIO相关介绍

    文章目录 GPIO概念TXD与RXD GPIO的使用注意STM32IO口哪些兼容5V一定不要接超过5V的电压默认不能做输出的GPIO GPIO硬件原理图GPIO地址 GPIO的八种工作模式浮空输入带上拉输入带下拉输入模拟输入开漏输出推挽输出