基础外设GPIO

GPIO外设的概述

基础概念

GPIO(General Purpose Input/Output,通用输入/输出接口,通常简称为I/O口)外设是现代微控制器MCU中最基础、应用最广泛的核心接口模块,主要用于实现芯片与外部电子设备之间的双向交互。

STM32部分型号参数对比表,包含Timer functions、Flash size、RAM size、Package、ADC、DAC、I/Os等参数

作为MCU的标准配置,GPIO引脚本质上是一种可编程的数字信号接口,其核心功能是作为通用的数字输入或输出通道。通过灵活的软件配置,工程师可以根据具体应用场景将GPIO设置为多种工作模式,包括但不限于推挽输出、开漏输出、上拉/下拉输入、浮空输入等模式,从而满足不同外设的接口需求。

在实际应用中,GPIO引脚需要与各类外部设备(如传感器、执行器、显示器等)建立物理连接,通过这种连接方式,MCU既可以向外设发送控制指令,也能够实时采集外设的状态数据,实现双向的数据交换和通信功能。这种简单而强大的接口特性,使得GPIO成为嵌入式系统开发中最常用、最基础的外设接口之一。

电平概念

在嵌入式系统开发中,I/O口操作是一项基础且重要的功能,它主要涉及对芯片某个输入/输出端口进行电平控制与状态检测。具体而言,这种操作包含两个核心功能:一方面是通过编程控制I/O口的输出电平,可以将其设置为高电平或低电平状态,另一方面则是利用I/O口的输入功能,实时读取外部电路输入的电平状态。

电平是衡量电信号强弱或状态的物理量,通常以电压或电流的相对大小表示,是电子电路中传递信息的基础。在电子技术中,电平本质是电路中某点与参考点(通常为地)之间的电压差(电压电平)或电流值(电流电平),用于描述信号的幅度范围、逻辑状态或能量强弱,所以电平并不是一个非常准确的值,而是可以用一个范围表示。

正逻辑与负逻辑示意图

电平是相对概念,需要通过与基准值(例如电源电压、地电位)对比来定义高/低或具体数值,在数字电路中,电平主要用于表示逻辑状态(0/1),其中又可以把电平分为正逻辑电平和负逻辑电平。

电平规范

常用的电平规范包含TTL和CMOS电平,其中TTL电平基于双极型晶体管的电流驱动逻辑,以5V固定电压供电,功耗较高且抗干扰能力较弱,曾是早期数字电路主流,如今已逐步被替代,而CMOS电平采用MOS管互补结构的电压驱动设计,支持3.3V、1.8V等灵活电压,功耗极低且抗干扰能力强,凭借低功耗与高兼容性成为现代电路核心。

逻辑电平 Vcc/V VOH/V VOL/V VIH/V VIL/V
TTL 5.0 ≥2.4 ≤0.4 ≥2.0 ≤0.8
LVTTL 3.3 ≥2.4 ≤0.4 ≥2.0 ≤0.8
CMOS 5.0 ≥4.45 ≤0.5 ≥3.5 ≤1.5
LVCMOS 3.3 ≥3.2 ≤0.1 ≥2.0 ≤0.7

在当前嵌入式开发中,CMOS电平的低电压版本是绝对主流,广泛应用于微控制器(例如STM32)、传感器、通信接口(如UART、SPI、I2C)等场景,其灵活的电压适配与低功耗特性完美契合嵌入式设备对能效和小型化的需求,而传统TTL电平仅在部分老旧工业设备中偶尔使用,目前已经不是开发首选电平规范。

其他常用的电平规范

常用电平规范:逻辑定义与电气特性汇总表

电平规范 传输方式 逻辑1定义 逻辑0定义 核心电气参数
TTL 单端 输出(VOH)≥2.4V;输入(VIH)≥2.0V 输出(VOL)≤0.4V;输入(VIL)≤0.8V 供电电压(Vcc)=5.0V;单端对地参考
LVTTL 单端 输出(VOH)≥2.4V;输入(VIH)≥2.0V 输出(VOL)≤0.4V;输入(VIL)≤0.8V 供电电压(Vcc)=3.3V;兼容TTL输入逻辑
CMOS 单端 输出(VOH)≥4.45V;输入(VIH)≥3.5V 输出(VOL)≤0.5V;输入(VIL)≤1.5V 供电电压(Vcc)=5.0V;高噪声容限;低功耗
LVCMOS 单端 输出(VOH)≥3.2V;输入(VIH)≥2.0V 输出(VOL)≤0.1V;输入(VIL)≤0.7V 供电电压(Vcc)=3.3V;主流嵌入式电平;低功耗、高兼容性
RS232 单端 电压范围:-3~-15V(MARK状态) 电压范围:+3~+15V(SPACE状态) 参考地(GND);不确定区:-3~+3V;最大负载电容2500pF
RS422 差分 电压差(V_A - V_B)≥+0.2V 电压差(V_A - V_B)≤-0.2V 共模电压范围:-7~+7V;输出摆幅±2V(典型);驱动电流±15mA;特性阻抗120Ω
RS485 差分 电压差(V_A - V_B)≥+0.2V 电压差(V_A - V_B)≤-0.2V 共模电压范围:-7~+12V;支持32个节点;特性阻抗120Ω;半双工差分传输
CAN 差分 隐性电平:V_H≈2.5V,V_L≈2.5V,差分电压≈0V 显性电平:V_H≈3.5V,V_L≈1.5V,差分电压≈2.0V 共模电压范围:-2~+7V;总线短路保护;显性电平优先级高于隐性电平;特性阻抗120Ω

管脚布局

STM32F407ZET6芯片管脚布局图

(1) 电源引脚
VCC、VDD、VSS、VDDA、VSSA、VREF+等都属于电源相关的引脚类别,它们在电路中主要负责为芯片和外围元器件提供稳定的工作电压,确保各个功能模块能够正常运作。其中VCC和VDD通常表示正电源输入端,VSS表示接地端,VDDA和VSSA是模拟电源引脚,VREF+则用于提供参考电压。

(2) 晶振引脚
PC14、PC15、PH0、PH1这几组引脚被设计为晶振接口引脚,主要用于连接外部晶振电路,为芯片提供精确的时钟信号。这些引脚具有复用功能,在不需要使用晶振的场合下,也可以重新配置为普通的GPIO或其他特殊功能引脚,增加了设计的灵活性。

(3) 复位引脚
NRST是专门用于系统复位的引脚,它采用低电平有效的复位方式,当该引脚接收到持续的低电平信号时,芯片会执行复位操作,使整个系统恢复到初始状态。这种设计有助于在系统出现异常时快速恢复。

(4) BOOT引脚
BOOT0是一个专用的启动模式选择引脚,而BOOT1则可作为普通功能引脚使用。这两个引脚共同作用,通过不同的电平组合可以设置芯片的自举模式,决定芯片上电后从哪个存储区域启动程序,这在系统调试和程序升级时非常有用。

(5) GPIO引脚
该芯片采用144引脚的封装形式,其中通用输入输出(GPIO)引脚就占用了114个。这些GPIO引脚具有高度的可配置性,可以通过软件设置为输入或输出模式,还能配置不同的工作参数,为外部设备提供灵活的接口方案。

(6) 下载引脚
PA13、PA14、PB3、PB4等引脚被分配为程序下载接口,支持JTAG和SWD两种主流的调试下载协议。这些引脚在系统开发阶段尤为重要,既可以用于程序烧录,也可用于在线调试,大大提高了开发效率。

引脚命名

在集成电路设计领域,芯片制造商在进行芯片架构规划时,会按照行业通用标准对芯片的管脚进行系统性的命名规范。这种命名体系通常遵循国际通用的惯例,具有高度的统一性和标准化特征。具体而言,输入输出端口(IO口)的命名一般以英文字母"P"(代表Pin,即引脚)作为前缀标识。为了便于管理和使用,这些IO口会被划分为若干个功能组(或称端口组),每个端口组采用从A到H的大写字母进行顺序命名标记。

根据行业标准实践,每个端口组内部会包含16个独立的物理引脚,这些引脚的编号采用数字编码方式,从0开始依次递增至15。

基于这套命名规则,可清晰表示各个端口组的引脚分布:

  • A端口组:PA0 ~ PA15
  • B端口组:PB0 ~ PB15
  • C、D、E、F、G、H等端口组遵循相同逻辑

确保整个芯片的引脚命名系统既规范又有条理,便于工程师在电路设计和调试过程中快速准确地定位和使用各个功能引脚。

引脚功能

在实际应用过程中,需要特别注意的是,微控制器芯片的每个引脚都具有多种不同的功能配置。通常情况下,默认状态下引脚都被设置为通用输入输出(GPIO)模式,这是最基本也是最常用的功能模式。

引脚定义相关表格,包含Table 6. Legend/abbreviations used in the pinout table、Table 7. STM32F40x pin and ball definitions (continued),涵盖引脚名称、引脚类型、复用功能、额外功能等信息

然而,除了基本的GPIO功能外,这些引脚往往还具有所谓的"第二功能"或"复用功能",这些功能包括但不限于UART通信、PWM输出、I2C接口等特殊功能。

特别需要强调的是:

  • 当需要使用模数转换(ADC)通道或数模转换(DAC)功能时,必须将相应引脚配置为模拟模式。
  • 其他情况下,若要将引脚作为非GPIO功能使用,则必须将引脚设置为复用模式。

由于不同芯片厂商、不同型号的芯片其引脚功能定义可能差异很大,因此在实际项目开发中,若要准确了解具体引脚的各项功能定义、工作模式及配置方法,必须仔细查阅该芯片的官方数据手册(Datasheet)或参考手册(Reference Manual)!!!!


GPIO外设的原理与应用

背景说明

想要学习硬件开发,通常都是从最简单的LED灯控制开始入手。这主要是因为LED作为最基本的外设元件,具有电路结构简单、操作直观的特点,非常适合作为硬件开发的入门实验。

通过点亮LED这个基础项目,可逐步掌握硬件开发的基本流程,包括电路连接、程序编写、调试测试等环节,进而理解外设控制的核心原理和方法。LED控制虽然看似简单,但却涵盖了硬件开发中最基础也是最重要的知识要点,为后续学习更复杂的外设控制打下了坚实的基础。

开发流程

(1) 查看原理图,找到控制发光二极管的芯片的IO口,LED0 -- PF9引脚,其他的引脚类似!!

LED相关原理图

(2) 分析原理图,理解硬件的控制原理(复杂外设还需要结合数据手册),输出低电平就亮

二极管的几种外形图

(3) 程序设计,根据ST公司提供的函数库(标准外设库/HAL库)的帮助手册学习开发流程

函数库文件目录结构
外设初始化和配置流程说明

以上内容是ST公司官方提供的关于标准外设库(Standard Peripheral Library)的外设配置和初始化流程说明。这套流程涵盖了MCU(微控制器)中所有常见外设的配置方法,包括GPIO、USART、ADC、TIM等外设模块,具有很高的通用性和普适性。

代码示例和参考案例

ST公司考虑到不同开发者在实际项目中的需求可能存在差异,为了帮助开发者快速上手并正确使用这些外设,特别编写了大量详细的代码示例和参考案例。这些案例不仅包含了基本功能实现,还覆盖了各种典型应用场景下的配置方法,开发者可以直接参考这些现成的代码来加速自己的项目开发,大大提升了开发效率和用户体验。

示例:控制开发板的4盏LED灯全部点亮

/**
  * @brief  LED的初始化
  * @param  None
  * @retval None
  */
void LED_Config(void)
{
  //1.定义GPIO外设的初始化结构体变量
  GPIO_InitTypeDef GPIO_InitStructure;

  //2.开启GPIOF、GPIOE端口的时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);

  //3.配置引脚为输出模式
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;

  //4.初始化GPIOF端口(PF9、PF10)
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
  GPIO_Init(GPIOF, &GPIO_InitStructure);

  //5.初始化GPIOE端口(PE13、PE14)
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
  GPIO_Init(GPIOE, &GPIO_InitStructure);
}

/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{
  //1.硬件初始化
  LED_Config();

  /* Infinite loop */
  while (1)
  {
    //2.输出低电平,LED点亮
    GPIOF->BSRRH = GPIO_Pin_9 | GPIO_Pin_10;
    GPIOE->BSRRH = GPIO_Pin_13 | GPIO_Pin_14;
  }
}

GPIO外设接口原理分析

ST公司提供的关于GPIO的案例代码中,调用了很多关于GPIO外设相关的函数接口以及数据类型,作为初学者必须认真分析代码,才能掌握GPIO外设的核心原理。按照ST提供的关于外设的初始化和配置的流程分析:

/**
  * @brief  LED的初始化
  * @param  None
  * @retval None
  */
void LED_Config(void)
{
    //1.定义GPIO外设的初始化结构体变量
    GPIO_InitTypeDef GPIO_InitStructure;
    
    //2.开启GPIOF端口的时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
    
    //3.配置PF9引脚为输出模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    
    //4.初始化GPIOF端口
    GPIO_Init(GPIOF, &GPIO_InitStructure);
}

/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{
    //1.硬件初始化
    LED_Config();
    
    /* Infinite loop */
    while (1)
    {
        //2.PF9引脚输出低电平,则LED点亮
        GPIOF->BSRRH = GPIO_Pin_9;
    }
}

GPIO外设初始化核心流程

定义外设的初始化结构体变量 + 打开外设时钟 + 对结构体成员进行赋值 + 初始化外设

定义GPIO初始化结构体变量

结构体类型定义在 stm32f4xx_gpio.h 中:

GPIO初始化结构体定义

ST文件命名规则stm32f4xx_xxx.cstm32f4xx_xxx.h(每个外设对应独立的源文件和头文件)

STM32F4外设文件列表

开启GPIO端口时钟

使用GPIO外设前必须打开对应端口时钟,ST提供的时钟使能函数如下:

时钟使能函数说明
RCC时钟使能函数定义

关键说明

  • MCU常用总线:AHB总线、APB1总线、APB2总线(外设挂载在总线上,便于拓展)
  • 总线频率限制:
    • AHB域最大频率:168MHz
    • 高速APB2域最大频率:84MHz
    • 低速APB1域最大频率:42MHz
  • GPIO外设挂载在 AHB1总线 上,需使用 RCC_AHB1PeriphClockCmd 函数
内核图

可以发现,GPIO外设都是挂载在AHB1总线下的,所以如果打算启用某个GPIO端口的时钟,就需要使用AHB1总线对应的函数

GPIO外设总线挂载地址
寄存器和时钟

配置GPIO结构体成员

结构体成员包括:引脚编号、工作模式、输出类型、内部电阻、输出速度

设置GPIO引脚编号

ST提供宏定义(16个引脚,0~15),采用16bit短整型(bit位对应引脚编号):

GPIO引脚宏定义

ST公司提供了一组宏定义用于配置GPIO端口下的16个引脚的,每个GPIO端口下的引脚都有编号,编号的范围是015,所以ST公司就采用了短整型(数据宽度是16bit,二进制的bit位编号刚好就是015,所以每个bit对应了端口下的1个引脚),注意:如果用户打算使用某端口下的某几个引脚,可以采用位或 | 实现引脚的配置。

  • 多引脚配置:使用位或 | 操作(例:GPIO_Pin_9 | GPIO_Pin_10
设置GPIO工作模式
GPIO工作模式枚举

工作模式中最常用的是输入/输出,想要掌握输入或者输出的原理,需要分析GPIO内部结构。

GPIO端口模式分类

GPIO端口模式分类
GPIO输出配置原理
  • 开漏模式:输出"0"激活N-MOS,输出"1"为高组态(Hi-Z)→ 仅能输出低电平,需外接上拉电阻输出高电平(适合IIC总线)
  • 推挽模式:输出"0"激活N-MOS,输出"1"激活P-MOS → 可直接输出高低电平,双向驱动,驱动能力强
设置GPIO输出类型
GPIO输出类型枚举
设置GPIO内部电阻
GPIO上下拉电阻枚举

上拉电阻和下拉电阻的主要作用是将引脚的电平稳定地拉高到高电平或者拉低到低电平,从而确保电路在没有外部信号输入时能够维持一个确定的状态。在MCU(微控制器)中,通常会集成内部的上拉或下拉电阻来实现这一功能。

  • 作用:稳定引脚电平(无外部信号时维持确定状态)
  • 特性:内部电阻阻值较大,驱动能力弱(弱上拉/弱下拉)→ 降低功耗、减少外部干扰
GPIO内部电阻原理
这些内部电阻的阻值一般设计得较大,因此它们所提供的驱动能力相对较弱。弱指的是这些电阻无法提供足够强的电流来快速改变引脚状态或者驱动较大的负载,因此被称为弱上拉或弱下拉。这种设计虽然在某些场景下可能显得能力不足,但它有助于降低功耗并减少对外部电路的干扰,是一种折中的解决方案。
设置GPIO输出速度
GPIO输出速度枚举

输出速度指的是的是引脚电平发生翻转时的速度情况。如果选择高速的模式,那么就必然会带来功耗以及噪声的增加。

这是因为在高速状态下,整个电路系统的工作强度和频率更高,各个部件之间的电流变化更剧烈,从而导致更多的能量消耗,并且由于电流快速变化而产生的电磁干扰等噪声也会相应增多。
在没有特殊要求或者不确定具体需求时,高速模式能够在大多数常见应用场景下满足基本的功能实现需求,保证系统的正常运行和信号的有效传输。

初始化GPIO外设

调用 GPIO_Init 函数,将结构体配置参数写入GPIO端口寄存器:

GPIO初始化函数说明

寄存器开发流程分析

不管是图形界面开发还是函数库开发,本质都是对硬件的寄存器进行配置,只不过函数库开发和图形界面开发都是ST公司的开发人员帮用户提前配置好了硬件的寄存器,并把配置过程封装在函数接口中,所以用户可以直接调用对应的函数接口来提高开发效率。

使用函数库开发(标准库 or HAL库)或者STM32CubuMX图形工具开发的缺点是代码运行效率比较低,因为函数内部可能嵌套调用其他函数,而且函数调用过程会占用堆栈空间。

所以项目对实时性要求比较高的情况下,开发人员应直接通过配置寄存器的方式控制硬件,目的是提高程序的运行效率以及节约内存。

寄存器开发7步流程

查看原理图,确定控制IO口

例:LED0 → PF9引脚

LED硬件原理图

分析硬件控制原理

例:LED灯 → 输出低电平点亮(结合二极管导通原理)

二极管结构原理

查阅参考手册,了解外设寄存器

STM32F4 GPIO相关寄存器列表

掌握寄存器使用流程

RCC_AHB1外设时钟使能寄存器(RCC_AHB1ENR)
GPIO端口模式寄存器(GPIOx_MODER)
GPIO端口输出类型寄存器(GPIOx_OTYPER)
GPIO端口输出速度寄存器(GPIOx_OSPEEDR)
GPIO端口上拉/下拉寄存器(GPIOx_PUPDR)
GPIO端口输出数据寄存器(GPIOx_ODR)
GPIO端口置位/复位寄存器(GPIOx_BSRR)

计算寄存器物理地址

寄存器物理地址 = 外设基地址 + 偏移地址

GPIOF端口寄存器地址计算

寄存器地址转为指针

32bitMCU的寄存器都是32bit,并且是没有符号位,所以如果计算出寄存器的物理地址,则需要把地址进行强制转换,应该使用 (unsigned int *)进行修饰。

注意:一般底层开发中,都需要配置硬件的寄存器,并且寄存器中存储的值会经常发生变化,所以为了提高程序的可靠性,必须在对寄存器地址进行强制转换的时候使用关键字volatile修饰。

【示例:绝对地址赋值】

// 设置绝对地址0x67a9的整型变量值为0xaa66
*(volatile unsigned int *)0x67a9 = 0xaa66;

volatile关键字的意思是易变的,一般是用于修饰变量的,如果变量使用volatile进行修饰,则表示防止内核优化该变量(优化指的是内核会为了提高运行效率,把经常使用的变量进行备份,备份内核中的缓存,内核不需要每次访问变量的时候,重新再去变量地址下查找)。

典型的应用场景:
1.多线程编程中,被多条线程共享的临界资源
2.访问硬件寄存器的时候,需要对寄存器进行修饰
3.在中断服务程序ISR中访问的全局变量

访问寄存器指针(解引用)

寄存器宏定义

通过寄存器实现控制PF9对应的LED灯点亮

寄存器实现LED灯点亮
寄存器实现LED灯点亮

温馨提示

ST公司是提供了和外设相关的函数接口,并且也会把每个外设的初始化流程详细的进行描述(例如想要打开外设的时钟,但是不清楚要调用哪个函数)。所以每个外设详细的初始化流程都被写在外设的驱动文件中,尤其是外设的源文件(例如stm32f4xx_gpio.c)的文件注释!

posted @ 2026-01-08 08:46  Jaklin  阅读(74)  评论(0)    收藏  举报