stm32 gpio*

gpio

把gpio,当成是一个外设,重新再学习一遍

stm32f1 gpio资源

STM32F103ZET6 芯片是 144 脚的芯片,

具有 GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF 和 GPIOG 七组 GPIO 口,共有 112 个 IO 口可供我们编程使用

IO 功能模式

GPIO 有八种工作模式,分别是:

上下拉(输入)

  1. 输入浮空
    • 拉/下拉电阻为断开状态
    • 用于按键检测等场景
  2. 输入上拉
  3. 输入下拉
  4. 模拟输入
    • 用于 ADC、DAC、MCO这类操作模拟信号的外设

alt text

为什么要上下拉?(防止“漂移”)

简单来说,上下拉电阻的作用就是给引脚一个确定的默认状态,防止电路在悬空时受到电磁干扰

推挽开漏(输出、复用)

  1. 开漏输出

    • 它只能输出低电平 Vss 或者高阻态,常用于 IIC 通讯(IIC_SDA)或其它需要进行电平转换的场景
  2. 推挽输出

    • 会输出低电平 VSS 或者高电平VDD,驱动能力较强
  3. 开漏式复用功能

    • 。当选择复用功能时,引脚的状态是由对应的外设控制,而不是输出数据寄存器
  4. 推挽式复用功能

[!IMPORTANT]
1. 必须用【推挽输出】 (Push-Pull)

当你需要引脚作为 “动力源” 或者 “高速信号源” 时。

  • 驱动能力需求:如果你要直接驱动 LED、蜂鸣器、光耦,或者给某个模块供电。推挽输出在输出“1”时能提供稳定的电流,输出“0”时能强力吸收电流。
  • 高速通讯:比如 SPI 协议或 UART 串口。推挽输出的电平切换速度极快,边缘非常陡峭,能支持几十 MHz 的频率。
  • 简单驱动:控制继电器模块、驱动各种 LCD/OLED 屏幕。

[!IMPORTANT]
2. 必须用【开漏输出】 (Open-Drain)

当你需要 “兼容性”、“安全性” 或者符合 “总线标准” 时。

A. I2C 总线(必须开漏)

I2C 协议规定了多个设备挂在同一根线上。如果用推挽,一旦两个芯片一个发高一个发低,就会直接短路烧毁。开漏模式下,大家只负责“拉低”或“松手”,即使同时动作也不会坏。

B. 电平转换(3.3V 控 5V)

STM32 内部最高只能出 3.3V。如果你要把信号给一个 5V 的老芯片(它可能规定 3.5V 以上才算高电平),推挽就没戏了。

  • 做法:选开漏,外部接一个电阻拉到 5V。这样引脚“松手”时,电压就是 5V,完美解决。

C. “线与”逻辑(报警/中断线)

比如你有 5 个传感器,任何一个报警都要让单片机知道。

  • 你可以把它们的开漏输出全部连在一起。
  • 只要有一个传感器报警(拉低),整条线就变低。这种简单的并联在推挽模式下是绝对禁止的。

[!IMPORTANT]
当你面对一个 GPIO 输出配置时,按顺序问自己这三个问题:

  1. 这是一根通讯总线吗?
  • 是 I2C 吗? 开漏。
  • 是 SPI/UART 吗? 推挽。
  1. 这根线上会连着多个输出设备吗?
  • 是(比如共享中断信号线) 开漏。
  • 否(一对一连接) 往下看。
  1. 对方的电压和 STM32 一样吗?
  • 一样 (3.3V) 推挽。
  • 对方更高 (5V) 开漏 + 外部上拉到 5V。

寄存器

在理解了IO的工作模式后,就知道要怎么用了,之后就是通过配置寄存器,来达成我们想要的工作模式。

所有的寄存器,简单来说,可以归结为:

  1. CR(控制寄存器)
  2. DR(数据寄存器)
  3. SR(状态寄存器)

CR

  • 这 2 个配置寄存器就是用来配置 GPIO 的相关工作模式和工作速度

alt text

DR

  • 这里就是输出的数据寄存器了,这里有两个,都可以进行写,具体会有区分,这些都属于具体的设备细节了,无需在意。

alt text

alt text

实验

点灯实验,没什么好说的,比较简单,这里要注意一个通用的意识:

初始化外设的步骤:

  1. 使能设备(使能时钟)
  2. 配置外设寄存器(有定义外设基地址)
/**
* @brief 初始化 LED 相关 IO 口, 并使能时钟
* @param 无
* @retval 无
*/
void led_init(void)
{

LED0_GPIO_CLK_ENABLE(); /* LED0 时钟使能 */
LED1_GPIO_CLK_ENABLE(); /* LED1 时钟使能 */


GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = LED0_GPIO_PIN; /* LED0 引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct); /* 初始化 LED0 引脚 */
gpio_init_struct.Pin = LED1_GPIO_PIN; /* LED1 引脚 */
HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct); /* 初始化 LED1 引脚 */
LED0(1); /* 关闭 LED0 */
LED1(1); /* 关闭 LED1 */
}