没志青年
发布于 2025-06-13 / 42 阅读
1

32单片机 - GPIO

在硬件确实连接的情况下,gpio没有输出,可能的原因:

  • 时钟没开

  • 没正确初始化

  • 一些特殊引脚如PC13不适合作为gpio,灌电流能力弱啥的

  • 一标准库中默认的是复用功能,需显式的修改为gpio功能

一个案例:使用spi功能

虽然PB15没有复用

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_0);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_0);

但是:

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_RxOnly;  

设置了双线,PB15就被强制复用了。

所以PB15虽然初始化了GPIO,但还是用不了,改为单线接收既可。

GPIO 概念

GPIO(General-purpose input/output)为通用输入输出端口,是所有的 MCU 必不可少的外设之一。

GPIO、端口、引脚的概念:

  • 引脚(Pin):物理硬件接口,如 PA1

  • 端口(Port):将引脚按逻辑分组,由寄存器统一控制,如 GPIOA

  • GPIO:引脚最基本的输出输出功能。

引脚复用,是指将引脚配置为其它外设的功能,如ADC、定时器等,最大化引脚利用率。

GPIO 工作模式:

高低电平定义:

以通过软件配置来进行控制,,,GPIO引脚需要与外设连接在一起,以实现和外设的通信、控制以及采集和捕获等功能。

高低电平的定义:一般电平采用都是TTL电平信号,TTL电平信号规定:+5V等价于逻辑“1”,0V等价于逻辑“0”,但实际上电平有一定的范围:>2.4V就表示高电平,<0.4V就表示低电平。

查看引脚功能

引脚类型:

  • S:电源供应引脚

  • I:仅输入

  • I/O:输入/输出

引脚特性:

  • TTa:可以容忍3.3V,且支持ADC/DAC模拟功能。

  • FT:可以容忍5V,FT的也支持ADC/DAC模拟功能啊,所以就很奇怪,为什么上面的不直接写成TT。

  • B:BOOT0引脚

  • RST:复位引脚,内部有弱上拉电阻。

复用功能和附加功能是互斥的,使用复用功能或附加功能后,引脚就无法再手动控制了。

  • 复用功能:外设功能复用,通过 GPIOx_AFR 寄存器选择。

  • 附加功能:引脚特殊的功能,这个功能不受GPIO相关的寄存器控制,而是由对应的外设寄存器直接控制的。比如ADC1将PA7(附加功能为ADC12_IN7)引脚作为输入引脚。就是你想要使用这个功能,不是通过GPIO的寄存器设置的,是使用外设的寄存器直接设置的。

(1)对于F405、F407系列

在数据手册(Datasheet)中找到【pin and ball definitions】

(2)对于F103x、F051x系列

在数据手册(Datasheet)中找到【pin definitions】

区别在于:

  • 对于F4:引脚初始化的时候就设置复用了。

  • 对于F1:开启了外设后,它有默认的引脚。引脚初始化后需手动重映射。

对于引脚的复用,有专门的表,看起来更方便:

重映射就是改变外设默认的引脚,前提是修改的那些引脚具有外设的功能。

GD32:

Datasheet:

(1)查找外设可用的引脚

比如搜索 TIMER1_CH

假设根据项目,选择了 PB11 引脚。

(2)根据 PB11 引脚,在表中找到对应外设(TIMER1)的复用号 AF?

GPIO 模式

输入:

  • 浮空输入(默认)

  • 模拟输入

  • 上拉输入

  • 下拉输入

输出:

  • 推挽输出

  • 开漏输出

  • 复用推挽输出

  • 复用开漏输出

GPIO 操作

引脚初始化

工作模式

工作速度:边沿转换速度。

应用场景

推荐速度

说明

LED控制、按键检测

2MHz~10MHz

信号边沿平缓,抗干扰强,适合长线传输或高噪声环境(如工业控制)。

UART、单线传感器(DHT11)

10MHz

中等速度,兼顾稳定和响应。

SPI(≤10MHz)、普通PWM

50MHz

确保信号边沿陡峭,减少畸变。

高频PWM(>20kHz)、SDIO

100MHz

避免信号延迟导致时序错误,但会增加功耗和电磁干扰EMI。

上拉电阻

完整代码:



// 多个引脚使用|分隔

F0 标准库:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB, ENABLE);


GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;                            
gpio_init_struct.GPIO_Mode = GPIO_Mode_OUT;                      
gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;                 
gpio_init_struct.GPIO_OType = GPIO_OType_PP;                   
gpio_init_struct.GPIO_PuPd = GPIO_PuPd_UP;                 
GPIO_Init(GPIOB, &gpio_init_struct);

引脚重置

读引脚

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

写引脚

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)

例如:

// 置 1
HAL_GPIO_WritePin(GPIOn, GPIO_PIN_x |GPIO_PIN_x, GPIO_PIN_SET);
// 清 0
HAL_GPIO_WritePin(GPIOn, GPIO_PIN_x |GPIO_PIN_x, GPIO_PIN_RESET);

翻转电平

void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

例如:

HAL_GPIO_TogglePin(GPIOn, GPIO_PIN_x);

使用寄存器方式操作GPIO

寄存器方式设置引脚输入输出:

#define SDA_IN()                                                               \
  {                                                                            \
    GPIOB->MODER &=~ (3 << (7 * 2));                                           \
    GPIOB->MODER |= 0 << 7 * 2;                                                \
  } // 输入模式

一个引脚使用两位控制,这里是PB7,所以是第14、15位。

3 为二进制的11,先清除,再设置。

寄存器方式设置引脚输出电平:

表示寄存器地址:

#define GPIO1_DR 			*((volatile unsigned int *)0X0209C000)
  1. (unsigned int *)0X0209C000:0X0209C000 只是一个数字而已,将其强制转换为一个指针类型的表达式,就像指针变量,指针变量的值为 0X0209C000

  2. (volatile unsigned int *)0X0209C000:volatile 告诉编译器指向的 unsigned int 随时可能被硬件修改,禁止优化。

  3. *((volatile unsigned int *)0X0209C000):指针解引用,取 0X0209C000 地址处的值。

((volatile unsigned int )0x0209C000) 是一个左值表达式,所以能GPIO1_DR = 1;这种写法

相关寄存器